Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

I have been working on this for the past two weeks not and for some reason I cannot seem to get nginx's try_files to work with my wordpress permalinks. I am hoping someone will be able to tell me where I am going wrong and also hopefully tell me if I made any major errors with my configurations as well (I am an nginx newbie... but learning :) ).

Here are my Configuration files

nginx.conf

user www-data;
worker_processes 4;
pid /var/run/nginx.pid;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    ##
    # Logging Settings
    ##

    # Defines the cache log format, cache log location
    # and the main access log location.
    log_format cache '***$time_local '
            '$upstream_cache_status '
            'Cache-Control: $upstream_http_cache_control '
            'Expires: $upstream_http_expires '
            '$host '
            '"$request" ($status) '
            '"$http_user_agent" '
            ;

    access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;

}

mydomain.com.conf

server {
    listen 123.456.78.901:80; # IP goes here.
    server_name www.mydomain.com mydomain.com;
    #root /var/www/mydomain.com/prod;
    index   index.php;

## mydomain.com -> www.mydomain.com (301 - Permanent)
    if ($host !~* ^(www|dev))
    {
            rewrite ^/(.*)$ $scheme://www.$host/$1 permanent;
    }

# Add trailing slash to */wp-admin requests.
    rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# All media (including uploaded) is under wp-content/ so
    # instead of caching the response from apache, we're just
    # going to use nginx to serve directly from there.
    location ~* ^/(wp-content|wp-includes)/(.*)\.(jpg|png|gif|jpeg|css|js|m$
            root    /var/www/mydomain.com/prod;
    }

# Don't cache these pages.
    location ~* ^/(wp-admin|wp-login.php)
            {
        proxy_pass http://backend;
    }

location / {
    if ($http_cookie ~* "wordpress_logged_in_[^=]*=([^%]+)%7C") {
            set $do_not_cache 1;
    }
    proxy_cache_key "$scheme://$host$request_uri $do_not_cache";
    proxy_cache main;
    proxy_pass http://backend;
    proxy_cache_valid 30m; # 200, 301 and 302 will be cached.
    # Fallback to stale cache on certain errors.
    # 503 is deliberately missing, if we're down for maintenance
    # we want the page to display.
    #try_files $uri $uri/ /index.php?q=$uri$args;
    #try_files $uri =404;
    proxy_cache_use_stale error
            timeout
            invalid_header
            http_500
            http_502
            http_504
            http_404;
    }

# Cache purge URL - works in tandem with WP plugin.
#        location ~ /purge(/.*) {
#           proxy_cache_purge main "$scheme://$host$1";
#        }

# No access to .htaccess files.
    location ~ /\.ht {
            deny  all;
    }

} # End server

gzip.conf

    # Gzip Configuration.
    gzip on;
    gzip_disable msie6;
    gzip_static on;
    gzip_comp_level 4;
    gzip_proxied any;
    gzip_types text/plain
           text/css
           application/x-javascript
           text/xml
           application/xml
           application/xml+rss
           text/javascript;

proxy.conf

# Set proxy headers for the passthrough
    proxy_redirect     off;

    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_max_temp_file_size 0;

    client_max_body_size       10m;
    client_body_buffer_size    128k;

    proxy_connect_timeout      90;
    proxy_send_timeout         90;
    proxy_read_timeout         90;

    proxy_buffer_size          4k;
    proxy_buffers              4 32k;
    proxy_busy_buffers_size    64k;
    proxy_temp_file_write_size 64k;
    add_header X-Cache-Status $upstream_cache_status;

backend.conf

upstream backend {
        # Defines backends.
        # Extracting here makes it easier to load balance
        # in the future. Needs to be specific IP as Plesk
        # doesn't have Apache listening on localhost.
        ip_hash;
        server 127.0.0.1:8001; # IP goes here.
}

cache.conf

# Proxy cache and temp configuration.
proxy_cache_path /var/www/nginx_cache levels=1:2
                 keys_zone=main:10m
                 max_size=1g inactive=30m;
proxy_temp_path /var/www/nginx_temp;

    proxy_cache_key "$scheme://$host$request_uri";
proxy_redirect off;

    # Cache different return codes for different lengths of time
# We cached normal pages for 10 minutes
proxy_cache_valid 200 302  10m;
proxy_cache_valid 404      1m;

The two commented out try_files in location \ of the mydomain config files are the ones I tried. This error I found in the error log can be found below.

 ...rewrite or internal redirection cycle while internally redirecting to "/index.php"

Thanks in advance

share|improve this question
add comment

3 Answers 3

That looks like a terribly complicated setup.

My first thought is to advise you to get rid of Apache entirely; nginx + php-fpm is perfectly capable of serving your WordPress blog, and most of the WordPress nginx rules you will find expect nginx to be used as the origin server rather than a reverse proxy to Apache.

Now, on with the show...

You can't use try_files in the same location block where you are passing the request upstream. It would just be ignored since everything is going to the proxy anyway. If you want to make this work, I suggest using a named location. Something like this (off the top of my head):

root /var/www/whatever;

location @apache {
    # proxy directives here
}

location / {
    try_files $uri $uri/ @apache;
}

The idea here is that you let nginx handle stuff that nginx can handle first, and then pass everything else upstream.

Of course, add in any additional directives you need, but the idea is to start simple, make sure the basic stuff works, and only then add stuff in to rewrite static page requests, cookies, etc.

share|improve this answer
    
There is a bit of success now, the home page loads and does not generate a 500 error anymore, but the rewritten pages now give a 404 error. –  tcherokee Jul 29 '12 at 11:07
add comment

As Michael Hampton says, you've got a rather complicated setup there and may well be better off keeping things simple and getting things working first. Here's my basic WordPress setup for nginx (which works with pretty permalinks):

root /var/www/mydomain.com/prod;
index index.php;

location / {
    # This is cool because no PHP is called for static content
    # The key thing here is passing the $args to index.php
    try_files $uri $uri/ /index.php?$args;
}

location ~ \.php$ {
    # Zero-day exploit defense
    # http://forum.nginx.org/read.php?2,88845,page=3
    # Won't work properly (404 error) if the file is not stored on this
    #server, which is entirely possible with php-fpm/php-fcgi.
    # Comment the 'try_files' line out if you set up php-fpm/php-fcgi on
    #another machine.  And then cross your fingers that you won't get hacked.
    try_files $uri =404;

    # For caching advice, see Mark Jaquith's superb post here:
    # http://markjaquith.wordpress.com/2012/05/15/how-i-built-have-baby-need-stuff/

    # To do some super-fancy rate limiting for the backend PHP requests, 
    # declare the following limit_req_zone in the http {} block of your nginx.conf e.g.
    # limit_req_zone $binary_remote_addr zone=appserver:1m rate=2r/s;
    # Then uncomment the below limit_req_zone line
    ## Set the request zone to limit backend DoSsing
    # limit_req zone=appserver burst=6;

    include /etc/nginx/proxy.conf;
    proxy_pass http://backend;
}

That's it – nice and simple. You don't need to do any of the following from your old config any more: (try_files will get nginx to try to serve it directly, then try the next parameter option, then the next, etc.)

# # Add trailing slash to */wp-admin requests.
# rewrite /wp-admin$ $scheme://$host$uri/ permanent;

# # All media (including uploaded) is under wp-content/ so
# # instead of caching the response from apache, we're just
# # going to use nginx to serve directly from there.
# location ~* ^/(wp-content|wp-includes)/(.*)\.(jpg|png|gif|jpeg|css|js|m$
#         root    /var/www/mydomain.com/prod;
# }

# # Don't cache these pages.
# location ~* ^/(wp-admin|wp-login.php)
#         {
#     proxy_pass http://backend;
# }

To ease with testing, open up a terminal, SSH into your server and run tail -f /var/log/nginx/*.log (or wherever your logs are stored), then watch for errors. Don't forget to restart or reload the server after you've made config changes!

Also, depending on which version of nginx you're running, and on which OS (I'm guessing Debian or Ubuntu from your /var/www/ path), you may be able to run /etc/init.d/nginx configtest which will test your config file and report helpful error messages about any mistakes – this will make life much easier.

Once you've got the above working and tested, here are a few cool tips like adding expires & caching headers to uploaded images; IP restrictions, rate limiting and extra logging for admin access and login attempts; etc.:

location ~ ^/wp-content/uploads/.+\.(jpe?g|gif|png|ico|bmp)$ {
    # set a expires to max as per http://developer.yahoo.com/performance/rules.html/#expires
    expires max;
    add_header Cache-Control public;
}

# Make sure you avoid the static cache when logging in or doing admin
location /wp-admin {    
    #  Restrict by IP for extra security (I'd also highly recommend the Login Lockdown plugin or the Duo Two-Factor Authentication plugin)
    #  allow 81.128.0.0/11; # BT Central Plus
    #  allow 86.128.0.0/10; # BT Central Plus
    #  deny all;
    access_log   /var/log/nginx/wp-admin.log;
    error_log   /var/log/nginx/wp-admin.error.log error;
    include /etc/nginx/proxy.conf;
    proxy_pass http://backend;
}

# Make sure you avoid the static cache when logging in or doing admin   
location = /wp-login.php { 
    #  Restrict by IP for extra security
    #  allow 81.128.0.0/11; # BT Central Plus
    #  allow 86.128.0.0/10; # BT Central Plus
    #  deny all; 
    # Declare the zone in the http {} block of your nginx.conf  e.g.
    #   limit_req_zone $binary_remote_addr zone=appserver:1m rate=2r/s;
    # Then uncomment the below line to limit hits to wp-login to 2 per second  (plus a burst of 2 using the leaky bucket model)
    # limit_req zone=appserver burst=2 nodelay;
    access_log  /var/log/nginx/login-attempts.log;
    include /etc/nginx/proxy.conf;
    proxy_pass http://backend;
}
share|improve this answer
add comment

I am running many WordPress sites using Nginx and PHP-FPM. This configuration seems to work fine for me for each site in the conf.d directory... (removed specific details for security).

server {
    listen       123.123.123.123:80;
    server_name  example.com;

    client_max_body_size 128m;

    root   /app/<user>/www;
    index  index.html index.htm index.php;

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

    location ~ \.php$ {
        fastcgi_pass   unix:/tmp/php-fpm.example.com.sock;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.