Nginx locations: Exempt certain location from maintenance mode - symfony

I have a Symfony 3.4 application running on nginx and a maintenance mode is implemented the following way in nginx:
location / {
# try to serve file directly, fallback to app.php
try_files $uri /app.php$is_args$args;
}
location ~ ^/(app)\.php(/|$) {
if (-f "/var/www/mysite.com/web/maintenance.html") {
return 503;
}
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
error_page 503 #maintenance;
location #maintenance {
rewrite ^(.*)$ /maintenance.html break;
}
I would like to exempt a certain location from maintenance mode, so that this location keeps being served by the Symfony application. So that requests to mysite.com/still/available/route are not redirected to the maintenance page.
How can that be done?

I found the answer, actually, it was quite obvious. One just needs to put the "if" into the root location block (/) and add another location block with the path that is supposed to remain accessible during maintenance mode. There, of course, must not appear that if-clause:
location / {
# try to serve file directly, fallback to app.php
if (-f "/var/www/mysite.com/web/maintenance.html") {
return 503;
}
try_files $uri /app.php$is_args$args;
}
location /still/available/route {
try_files $uri /app.php$is_args$args;
}
location ~ ^/(app)\.php(/|$) {
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
error_page 503 #maintenance;
location #maintenance {
rewrite ^(.*)$ /maintenance.html break;
}

Related

Nginx giving 404 error for WordPress' /wp-admin/ root

I have a Symfony2 application running alongside WordPress - mysite.com/blog routes to my /var/www/mysite/wordpress/ directory and everything else routes to /var/www/mysite/symfony:
server {
listen 80;
server_name mysite.com
location / {
try_files $uri /app.php$is_args$args;
}
location ~ ^/app\.php(/|$) {
fastcgi_pass 127.0.0.1:9000;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
internal;
}
location /blog {
root /var/www/mysite/wordpress;
rewrite ^/blog/(.+)$ /$1 break;
try_files $uri $uri/ /blog/index.php?q=$uri&$args;
index index.php;
location ~ \.php {
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
fastcgi_split_path_info ^(?:\/blog)(.+\.php)(.*);
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
}
Everything works fine, except the WordPress admin (mysite.com/blog/wp-admin/) gives me a 404 error. Visiting mysite.com/blog/wp-admin/index.php works as expected, so it looks like the index index.php line is not working. What could be the issue here?
You should use alias instead of root directive:
location ^~ /blog {
alias /var/www/mysite/wordpress;
index index.php;
try_files $uri $uri/ /blog/index.php?q=$uri&$args;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
include fastcgi_params;
fastcgi_split_path_info ^(?:\/blog)(.+\.php)(.*);
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
You need to edit the nginx server Configuration.
# WordPress single blog rules.
# Designed to be included in any server {} block.
# This order might seem weird - this is attempted to match last if rules below fail.
# http://wiki.nginx.org/HttpCoreModule
location / {
try_files $uri $uri/ /index.php?$args;
}
# Add trailing slash to */wp-admin requests.
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
# Directives to send expires headers and turn off 404 error logging.
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off; log_not_found off; expires max;
}
# Uncomment one of the lines below for the appropriate caching plugin (if used).
#include global/wordpress-wp-super-cache.conf;
#include global/wordpress-w3-total-cache.conf;
# Pass all .php files onto a php-fpm/php-fcgi server.
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
# This is a robust solution for path info security issue and works with "cgi.fix_pathinfo = 1" in /etc/php.ini (default)
include fastcgi.conf;
fastcgi_index index.php;
# fastcgi_intercept_errors on;
fastcgi_pass php;
}
More about this here:
http://codex.wordpress.org/Nginx#General_WordPress_rules
So, if you allege that /blog/wp-admin/index.php works, but /blog/wp-admin/ doesn't, perhaps just conditionally append index.php, if need be?
+ rewrite ^/blog/wp-admin(/)?$ /wp-admin/index.php break;
rewrite ^/blog/(.+)$ /$1 break;
So, what do the logs say in regards to your 404? I think this might be related to the fact that the index directive causes "an internal redirect", thus I would not be surprised if your 404 ends up being generated through the / instead of the /blog location.

How can Nginx use try_files only if file exists?

I have fully worked example of virtual host for a project on Symfony from ()[].
There is a code:
server {
listen 80;
server_name localhost www.localhost;
root /vagrant/web;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
location / {
# try to serve file directly, fallback to app.php
try_files $uri /app_dev.php$is_args$args;
}
# DEV
# This rule should only be placed on your development environment
# In production, don't include this and don't deploy app_dev.php or config.php
location ~ ^/(app_dev|config)\.php(/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
# PROD
location ~ ^/app\.php(/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/app.php/some-path
# Remove the internal directive to allow URIs like this
#internal;
}
}
But I want to improve it a bit for me. How can I load maintenance.html by default instead of app_dev.php?
P.S. I need a behaviour like with try_files $uri /maintenance.html$is_args$args; instead of try_files $uri /app_dev.php$is_args$args;, but ONLY if maintenance.html exists
Solution from here with bit of comments
location / { #can be any location or even in server
if (-f $document_root/maintenance.html) {
return 503; #503 for search engines
}
... # the rest of your config goes here
}
error_page 503 #maintenance;
location #maintenance {
rewrite ^(.*)$ /maintenance.html break;
}
I solved it with:
try_files $uri /maintenance.html$is_args$args /app_dev.php$is_args$args;

nginx with multiple symfony2 apps

I saw that many people had problem configuring one nginx server to have multiple symfony2 applications. However, none wanted the same things and had the same problem as me.
What I want to do is to have multiple applications on the same domain. One main application will answer directly to the domain, and the others will be on alias subdirectory.
With a schema :
http://mydomain/ -> main app
http://mydomain/subdir1 -> another app
http://mydomain/subdir2 -> yet another app
I tried by myself to do that and the main app works perfectly. But the subdirectories are most of the time intercepted by the main app, which throws 404. When I try to add app.php in the URL of a subdirectory (like http://mydomain/subdir1/app.php/my/route), the server return 404.
This is what I did until now :
server {
listen 80;
server_name mydomain;
root /server/www/main-app/web;
location / {
# try to serve file directly, fallback to app.php
try_files $uri /app.php$is_args$args;
# PROD
location ~ ^/app\.php(/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
}
location /subdir1/ {
alias /server/www/other-app1/web;
# try to serve file directly, fallback to app.php
try_files $uri /server/www/other-app1/web/app.php$is_args$args;
# PROD
location ~ ^/other-app1/app\.php(/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
}
}
Thanks you for your help !
EDIT 26/12/2014 :
For those who did not understand exactly what I want : I want to host multiple symfony2 apps on the same domain name without subdomain. Without subdomain, I must use subdirectory. Before that I tried nginx, I used Apache2 and it was easy to do the trick with Alias.
I did more search and found out that "alias" and "try_files" aren't good friends (see this bug report : http://trac.nginx.org/nginx/ticket/97). So I activated debug mode and did many tests.
Now I almost did it. The main apps no longer intercepts subdirectories and the others apps answer.
But those others apps answer by 404 so I looked in their logs. And I found out that they looked for URL pattern with the subdirectory in it. For instance they searched /subdir1/login instead of /login.
So this is my new configuration :
server {
listen 80;
server_name mydomain;
root /server/www/main-app/web;
location #rewriteapp {
rewrite ^(.*)$ /app.php/$1 last;
}
location /subdir1/ {
set $root "/server/www/other-app1/web";
# try to serve file directly, fallback to app.php
try_files $uri #rewriteapp;
}
location / {
index app.php;
set $root "/server/www/main-app/web";
# try to serve file directly, fallback to app.php
try_files $uri #rewriteapp;
}
# PROD
location ~ ^/app\.php(/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
}
As you can see, the trick was to not use $document_root for the SCRIPT_FILENAME and I created my own instead. I don't know how the symfony2 router search the pattern in the URL, but with my previous configuration (Apache2) I never had this problem. So maybe their is another trick to send the correct path to script app.php.
Thanks you again for your help !
This solved it finally for me (thanks to Claros answer), after million things i tried. Like this, urls like the following work:
/abc/path/to/endpoint
but not /abc/app.php/path/to/endpoint. Config.php and App_dev.php, if in web folder are given back as plain text.
I still try to figure out to get /abc to work (/abc/ works but /abc not). There i get a Symfony exception that route /abc can not be found.
Also some font urls (for bootstrap) are still incorrect but styles, routing etc works.
location /abc {
set $subpath /abc;
set $sfPath /var/www/abc/current/web;
alias $sfPath;
try_files $uri #rewrite;
}
location / {
set $subpath "";
set $sfPath /var/www/def/current/web;
alias $sfPath;
try_files $uri #rewrite;
}
location #rewrite {
rewrite ^(.*)$ $subpath/app.php$1 last;
}
location ~ /app\.php(/|$) {
internal;
include /etc/nginx/fastcgi_params;
fastcgi_index app.php;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_param DOCUMENT_ROOT $sfPath;
fastcgi_param SCRIPT_FILENAME $sfPath/app.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
}
If you want also the app_dev.php to work in demo environment the best way i found is the following (to have the php block everytime inside the location block):
location /xyz {
set $subpath /xyz;
set $sfPath /var/www/xyz/current/web;
alias $sfPath;
try_files $uri #rewrite;
#Change the match for app_dev.php to work
location ~ /(app|app_dev|config)\.php(/|$) {
#Drop the internal for App_dev.php to work
#internal;
include /etc/nginx/fastcgi_params;
fastcgi_index app.php;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_param DOCUMENT_ROOT $sfPath;
fastcgi_param SCRIPT_FILENAME $sfPath/app.php;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
}
}
After many hours of debugging, I finally solved the problem. This is my final configuration :
server {
listen 80;
server_name mydomain;
root /server/www;
location #rewriteMainApp {
rewrite ^(.*)$ /app.php/$1 last;
}
location #rewriteOtherApp1 {
rewrite ^(.*)$ /subdir1/app.php/$1 last;
}
location /subdir1 {
alias /server/www/other-app1/web;
index app.php;
set $subfolder "other-app1/web";
try_files $uri #rewriteOtherApp1;
}
location / {
root /server/www/main-app/web;
index app.php;
set $subfolder "main-app/web";
try_files $uri #rewriteMainApp;
}
# PROD
location ~ /app\.php(/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/$subfolder/app.php;
}
}
Thanks you all for your help !
Seperate your applications with-in another server tag in your sites-enabled file.
For example:
#Site 1
server {
#Configuration
}
server {
#Configuration 2
}
server {
#Configuration 3
}
Sample configuration:
server {
listen 80;
root /var/www/yourdomain.com/web;
server_name yourdomain.com www.yourdomain.com;
add_header X-UA-Compatible "IE=Edge,chrome=1";
location ~* \.(css|js|gif|jpe?g|png)$ {
expires 1y;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location / {
try_files $uri #rewriteapp;
}
location #rewriteapp {
rewrite ^(.*)$ /app_dev.php/$1 last;
}
location ~ ^/(app|app_dev|config)\.php(/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
error_log /var/log/nginx/yourdomain.com.error.log;
access_log /var/log/nginx/yourdomain.com.access.log;
}
server {
listen 80;
root /var/www/yourdomain.com/anotherproject/web;
server_name sub1.yourdomain.com www.sub1.yourdomain.com;
add_header X-UA-Compatible "IE=Edge,chrome=1";
location ~* \.(css|js|gif|jpe?g|png)$ {
expires 1y;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location / {
try_files $uri #rewriteapp;
}
location #rewriteapp {
rewrite ^(.*)$ /app_dev.php/$1 last;
}
location ~ ^/(app|app_dev|config)\.php(/|$) {
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS off;
}
error_log /var/log/nginx/sub1.yourdomain.com.error.log;
access_log /var/log/nginx/sub1.yourdomain.com.access.log;
}

Nginx, PHP and rewrite

How do I make PHP-FPM rules play nicely with Nginx rewrite rules?
Current config file
server {
location / {
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
include fastcgi.conf;
}
if (!-e $request_filename){
rewrite ^(.*)$ /index.php?routestring=$1 break;
}
rewrite ^/(admincp/)$ /index.php?routestring=$1 break;
}
}
Change your location block to the following. Also try to avoid if statements, read about it (and more) here: http://wiki.nginx.org/Pitfalls
I've replaced the if (!-e ...) part with the #missing block in the config below.
server {
root /your/root/path
index index.php index.html index.htm;
server_name your.domain.com;
rewrite ^/(admincp/)$ /index.php?routestring=$1 break;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.php
try_files $uri $uri/ /index.php?$args;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
# Move to the #missing part when the file doesn't exist
try_files $uri #missing;
# Fix for server variables that behave differently under nginx/$
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Include the standard fastcgi_params file included with ngingx
include fastcgi_params;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_index index.php;
# Override the SCRIPT_FILENAME variable set by fastcgi_params
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_nam$
# Pass to upstream PHP-FPM; This must match whater you name you$
#fastcgi_pass phpfpm;
fastcgi_pass 127.0.0.1:9000;
}
location #missing {
rewrite ^(.*)$ /index.php?routestring=$1 break;
}
}

How to install symfony2 app in a subdirectory in nginx

I need to install multiple symfony2 applications on the same host but on different subdirectories (or location blocks).
With this config nginx throws a "file not found" or redirect loop message when trying to access any url.
Example:
/login -> /base/login
/app1 -> /base/app1
/app2 -> /base/app2
Current Config:
root /base/default; #Points to an empty directory
# Login Application
location ^~ /login {
alias /base/login/web;
try_files $uri app_dev.php;
}
# Anything else
location ~ ^/([\w\-]+) {
alias /base/$1/web;
try_files $uri app_dev.php;
}
location / {
# Redirect to the login
rewrite ^ /login redirect;
}
# Handle PHP
location ~ \.php$ {
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
fastcgi_param HTTPS off;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
After hours spended to find this (sf2 doc doesn't explain how cgi parameters are needed and interpreted, you need to go through Request.php to understand), so I share this.
This is a config which seems ok with sf2 in a directory {subdir} (and web access to others files than {subdir}/web/* prohibited).
It works for me with php-fpm (socket).
Of course, replace "{subdir}" by your /path/from/docroot/to/symfony_root/
dev environnement can be choosen by adding "dev" to "{subdir}" (since app_dev.php in the url no longer works with this conf)
server {
# general directives
location ~ ^/{subdir}(/.*)$ {
try_files /{subdir}/web$1 #sf2;
}
location ~ ^/{subdir}dev(/.*)$ {
expires off;
try_files /{subdir}/web$1 #sf2dev;
}
location #sf2 {
expires off;
fastcgi_pass {your backend};
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/{subdir}/web/app.php;
fastcgi_param SCRIPT_NAME /{subdir}/app.php;
fastcgi_param REQUEST_URI /{subdir}$1;
}
location #sf2dev {
expires off;
fastcgi_pass {your backend};
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/{subdir}/web/app_dev.php;
fastcgi_param SCRIPT_NAME /{subdir}dev/app_dev.php;
fastcgi_param REQUEST_URI /{subdir}dev$1;
}
# other location directives
# if some others apps needs php, put your usual location to cactch php here
}
I Hope it helps (and there isn't any misconfiguration), given without any guarantee...
Of course you can pick out prod/dev conf if you don't need. And you can use var and only one #sf2 location instead :
set $sf2_root /{subdir};
location ~ ^/{subdir}(/.*)$ {
set $sf2_prefix /{subdir};
set $sf2_ctrl app.php;
try_files $sf2_root/web$1 #sf2;
}
location ~ ^/{subdir}dev(/.*)$ {
set $sf2_prefix /{subdir}dev;
set $sf2_ctrl app_dev.php;
expires off;
try_files $sf2_root/web$1 #sf2;
}
location #sf2 {
expires off;
fastcgi_pass {your backend};
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$sf2_root/web/$sf2_ctrl;
fastcgi_param SCRIPT_NAME $sf2_prefix/$sf2_ctrl;
fastcgi_param REQUEST_URI $sf2_prefix$1;
}
Here is a simpler configuration for symfony2 on the "/front/" subdirectory.
Route generation and assets work fine.
The configuration
set $frontRoot /your/project/path/web;
set $sfApp app_dev.php; # Change to app.php for prod
location /front/ { # Static files
root $frontRoot;
rewrite ^/front/(.*)$ /$1 break;
try_files $uri #sfFront;
}
location #sfFront { # Symfony
fastcgi_pass phpfcgi;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $frontRoot/$sfApp;
fastcgi_param SCRIPT_NAME /front/$sfApp;
fastcgi_param REQUEST_URI /front$uri?$args;
fastcgi_param HTTPS off;
}
Some explanation
The trick is to make symfony believe app.php script is in /front/, so it generates routes and assets with this path.
I looked at what apache was giving to MOD-PHP to use the same values.
SCRIPT_FILENAME: The absolute path to the PHP file. Here, it is always /your/project/path/app_dev.php
REQUEST_URI: The URI the user entered. Here, we have to manually re-add /front at the begining of the path as it was removed by the file serving location (via rewrite ^/front/(.*)$ /$1 break;)
SCRIPT_NAME: The value is /front/app_dev.php. This is the most important part. Symfony will cut app_dev.php and prepend /front to all its routes.
Based on dcaillibaud's answer, here's a more generic configuration,
allowing to support multiple Symfony (version 2 or 3) projects under the root web path, using some regular expression magic:
server {
# other specific directives...
root /var/www;
location ~ ^/([^/]+)/dev(/.*)$ {
location ~ \.php { deny all; }
set $sf2_root /$1;
set $sf2_prefix /$1/dev;
set $sf2_ctrl app_dev.php;
expires off;
try_files $sf2_root/web$2 #sf2;
}
location ~ ^/([^/]+)(/.*)$ {
location ~ \.php { deny all; }
set $sf2_root /$1;
set $sf2_prefix /$1;
set $sf2_ctrl app.php;
try_files $sf2_root/web$2 #sf2;
}
location #sf2 {
expires off;
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$sf2_root/web/$sf2_ctrl;
fastcgi_param SCRIPT_NAME $sf2_prefix/$sf2_ctrl;
fastcgi_param REQUEST_URI $sf2_prefix$2;
}
}
So basically, you'll have:
www.domain.com/project pointing to your PROD environment
and
www.domain.com/project/dev pointing to your DEV environment
Please replace the line:
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
with your appropriate backend
This can also be easily adapted for newer Symfony projects using index.php script instead of app(_dev).php, changing the web/public path, and setting some ENV variables.
I think this approach, answers better the original question.

Resources