I'm trying to build a nginx-based maintenance mode application, that catches all requests to my applications and returns a predefined response as a 503.
I currently have applications requesting json responses as well as users accessing the pages with their browsers. So in case the request contains the header Accept: application/json
, I want to respond with the content of a json file maintenance.json
, otherwise with an html file maintenance.html
.
My current nginx config looks like this:
server {
listen 8080;
root /usr/share/nginx/maintenance;
server_tokens off;
error_page 503 = @unavailable;
location ^~ / {
return 503;
}
location @unavailable {
set $maintenanceContentType text/html;
set $maintenanceFile /maintenance.html;
if ($http_accept = 'application/json') {
set $maintenanceContentType application/json;
set $maintenanceFile /maintenance.json;
}
default_type $maintenanceContentType;
try_files $uri $maintenanceFile;
}
}
For browser requests to any path this works out fine: "https://maintenance.my-domain.local/some-path". I get the response code and the html content.
But for requests with header Accept: application/json
I get a 404 html page. And the nginx log shows [error] 21#21: *1 open() "/usr/share/nginx/maintenance/some-path" failed (2: No such file or directory), client: 10.244.2.65, server: , request: "GET /asd HTTP/1.1", host: "maintenance.my-domain.local".
It seems like json requests are ignoring my location
for some reason. When I remove the directive to set the appropriate file and just always return the html this also works for json-requests.
Anyone any idea?
I'm not necessarily looking for a fix for this specific config, but rather for something that fits my needs of responding with different "error pages" based on the Accept header.
Thanks in advance!
EDIT: For some reason this now results in an HTTP 200 instead of a 503. Don't know what I changed..
EDIT2: Managed to fix a part of it:
server {
listen 8080;
root /usr/share/nginx/maintenance;
server_tokens off;
location ^~ / {
if ($http_accept = 'application/json') {
return 503;
}
try_files /maintenance.html =404;
}
error_page 503 /maintenance.json;
location = /maintenance.json {
internal;
}
}
With this config I now get the maintenance page when using the browser and the maintenance json, when defining the header Accept: application/json
. The browser response code is 200 now though...
Ok, I found the solution to my problem.
# map the incoming Accept header to a file extension
map $http_accept $accept_ext {
default html;
application/json json;
}
server {
listen 8080;
root /usr/share/nginx/maintenance;
server_tokens off;
# return 503 for all incoming requests
location ^~ / {
return 503;
}
# a 503 redirects to the internal location `@maintenance`. the
# extension of the returned file is decided by the Accept header map
# above (404 in case the file is not found).
error_page 503 @maintenance;
location @maintenance {
internal;
try_files /maintenance.$accept_ext =404;
}
}
Key was the map on the top. I just added application/json
there and mapped everything else to the html file by default. But you could add multiple other files/file types there of course.