nginx; return response code if a file exists by using try_files only

后端 未结 1 681
萌比男神i
萌比男神i 2021-02-05 13:29

Since IfIsEvil I\'ve been trying to set up a configuration using the directive try_files only so that a maintenance page is displayed together with the response cod

1条回答
  •  名媛妹妹
    2021-02-05 14:04

    Very good question! But it's not possible at all unless you call some kind of script that sets the correct response code.

    A working solution only with nginx and no if

    The try_files directive is only performing an internal redirect for the last statement. But we can combine it with the index directive and force an internal redirect.

    # This will be the HTML file to display for the 503 error.
    error_page 503 /maintenance/maintenance.html;
    
    # Let nginx know that this particular file is only for internal redirects.
    location = /maintenance/maintenance.html {
      internal;
    }
    
    # Any request that starts with the maintenance folder is 503!
    location ^~ /maintenance/ {
      return 503;
    }
    
    # Instead of checking if a file exists and directly delivering it we check
    # if a certain directory exists and trigger our index directive which will
    # perform an internal redirect for us.
    location / {
      expires epoch;
      try_files /maintenance/ $uri $uri/ /index.php?$args;
    }
    

    Any drawbacks with this method?

    • Actual 301 redirect to the directory instead of staying at the same URL (search engines).
    • Browser caching of 301 redirects might be an issue, that's why I added the expires epoch to the location block.

    Other solutions?

    Via nginx configuration file

    Instead of creating an HTML file, why not create an nginx configuration file and simply reload the process?

    • Much better performance!
    • Easier to understand!
    • No side effects!

    The nginx configuration could look like the following (note that this if isn't evil at all):

    error_page 503 /maintenance.html;
    
    location / {
      include maintenance.conf;
      if ($maintenance = 1) {
        return 503;
      }
      try_files $uri $uri/ /index.php?$args;
    }
    

    Content of the maintenance.conf file:

    set $maintenance 0;
    

    And if you want to activate the maintenance mode (in your shell):

    echo set $maintenance 1;> maintenance.conf && service nginx reload
    

    More advanced for shell friends You could even extend an init script with this, for instance my LSB compliant one by replacing the following block at the end of the file:

    *)
      echo "Usage: ${NAME} {force-reload|reload|restart|start|status|stop}" >&2
      exit 1
    ;;
    

    With the following block:

    maintenance)
      echo "set $maintenance 1;" > /etc/nginx/maintenance.conf && service nginx reload;
    ;;
    
    production)
      echo "set $maintenance 0;" > /etc/nginx/maintenance.conf && service nginx reload;
    ;;
    
    *)
      echo "Usage: ${NAME} {force-reload|reload|restart|start|status|stop|maintenance|production}" >&2
      exit 1
    ;;
    

    And now you can simply execute the following command (including auto-completion) to go into maintenance mode:

    service nginx maintenance
    

    Or the following to go back into production:

    service nginx production
    

    With a script / PHP file

    Another extremely easy approach that would work like a charm is to use a PHP file that handles it.

    location / {
        try_files /maintenance.php $uri $uri/ /index.php?$args;
    }
    

    Your PHP file would look exactly like your HTML file, you only have to add the following to it's beginning (assuming PHP 5.4+):

    
    
    
    
    

    0 讨论(0)
提交回复
热议问题