7

I am trying to get NGINX to reverse proxy & provide SSL Termination for a WordPress site running on Apache at port 8086. I want NGINX to handle the static files, and proxy only PHP requests to Apache.

I have been successful in getting this to work using standard links. (i.e. https://example.com/?post=274 works correctly)

When I enable permalinks of any kind, the home page will load, as will wp-admin, but https://example.com/what-we-do/ fails.

Looking at the NGINX logs, I see

2018/05/23 09:36:40 [error] 7472#0: *1 "/var/www/example.com/live_site/what-we-do/index.php" is not found (2: No such file or directory), client: xxx.xxx.xxx.xxx, server: example.com, request: "GET /what-we-do/ HTTP/2.0", host: "example.com", referrer: "https://example.com/?post=274"

So NGINX is attempting to look for /permalink/index.php as a static path/file instead of passing to apache. Any thoughts on how to get this to work?

My NGINX Config looks like:

upstream example_apache {
    ip_hash;
    server 127.0.0.1:8086;
}

server {
# HTTP/HTTPS Server Block
# General Config
    listen                      [::]:80;
    listen                      80;
    listen                      [::]:443 http2 ssl;
    listen                      443 http2 ssl;
    server_name                 example.com
                                www.example.com;

    root                        /var/www/example.com/live_site;
    access_log                  /var/log/nginx/access-example.com.log main;
    error_log                   /var/log/nginx/error-example.com.log;
    index                       index.php;

#SSL Cert Configuration
# Check SSL config at https://www.ssllabs.com/ssltest/
    ssl_prefer_server_ciphers   on;
    ssl_protocols               TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers                 "ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-RSA-CHACHA20-POLY1305 EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH DHE-RSA-CHACHA20-POLY1305 EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4 !SEED !CAMELLIA";
    ssl_session_cache           shared:SSL:100m;
    ssl_session_timeout         180m;
    ssl_dhparam                 /var/www/certs/dh4096.pem;

    ssl_certificate             /var/www/certs/lets_encrypt/web01.example.com/web01.example.com.fullchain.secp384r1.cer;
    ssl_certificate_key         /var/www/certs/lets_encrypt/web01.example.com/web01.example.com.secp384r1.key;
    ssl_certificate             /var/www/certs/lets_encrypt/web01.example.com/web01.example.com.fullchain.rsa4096.cer;
    ssl_certificate_key         /var/www/certs/lets_encrypt/web01.example.com/web01.example.com.rsa4096.key;

# Enable HSTS #Deploy in stages to prevent extended loss to site.
    add_header                  Strict-Transport-Security "max-age=300; includeSubdomains;"; #300s-5min TTL Testing
    #add_header                 Strict-Transport-Security "max-age=604800; includeSubdomains;"; #1week TTL Testing
    #add_header                 Strict-Transport-Security "max-age=2592000; includeSubdomains;"; #1month TTL Testing
    #add_header                 Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; #10886400s-126days Min for Preload
    #add_header                 Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; #63072000s-2years Production Value

# OCSP Configuration
    ssl_trusted_certificate     /var/www/certs/lets_encrypt/web01.example.com/web01.example.com.fullchain.secp384r1.cer;
    ssl_stapling                on;
    ssl_stapling_verify         on;
    resolver                    8.8.4.4 8.8.8.8 valid=300s;
    resolver_timeout            10s;

# LetEncrypt webroot alias
    location /.well-known/acme-challenge/ {
        alias /var/www/le_root/.well-known/acme-challenge/;
    }
# www to non-www rewrite
# Redirect to the correct place, if needed
    set $https_redirect 0;
    if ($server_port = 80) { set $https_redirect 1; }
    if ($host ~ '^www\.') { set $https_redirect 1; }
    if ($https_redirect = 1) {
        return 301 https://example.com$request_uri;
    }

# Wordpress entry point
    location / {
        #Try                    file dir    index.php else 404
        try_files               $uri $uri/ /index.php?$args =404;

        #All Files except for *.php
        location ~ .+(?<!\.php)$ {
            location ~ ^[^.]+\.[^.]+$ {
                expires         max;
                add_header      Cache-Control public;
                break;
            }
        }

        #Only *.php files
        location ~ \.php$ {
            proxy_set_header    X-Real-IP           $remote_addr;
            proxy_set_header    Host                $host;
            proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
            proxy_set_header    X-Forwarded-Proto   $scheme;
            proxy_pass_header                       Set-Cookie;

            proxy_set_header    SSL_PROTOCOL        $ssl_protocol;
            proxy_set_header    SSL_CLIENT_CERT     $ssl_client_cert;
            proxy_set_header    SSL_CLIENT_VERIFY   $ssl_client_verify;
            proxy_set_header    SSL_SERVER_S_DN     $ssl_client_s_dn;

            proxy_pass                              http://example_apache;
        }
    }
}

Since this issue isnt even getting to the proxy pass part, and appears to be strictly NGINX related (that I can tell), the following doesnt apply. But someone will be wondering, or it may help others stumbling on this question to know the apache config side as well.

My apache has an .htaccess file containing:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

And my wp-config.php has:

// If WordPress is behind reverse proxy
// which proxies https to http
if ( (!empty( $_SERVER['HTTP_X_FORWARDED_HOST'])) ||
 (!empty( $_SERVER['HTTP_X_FORWARDED_FOR'])) ) {

$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];

$_SERVER['HTTPS'] = 'on';
}

And my apache config has:

<VirtualHost *:8086>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/example.com/live_site
ServerName  example.com
ServerAlias www.example.com


ErrorLog ${APACHE_LOG_DIR}/example.com.error.log
CustomLog ${APACHE_LOG_DIR}/example.com.access.log combined

Alias "/.well-known/acme-challenge/" "/var/www/le_root/.well-known/acme-challenge/"

<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory /var/www/example.com/live_site>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    allow from all
</Directory>

</VirtualHost>

I should also note, when I connect directly to Apache I can see all of the page permalinks correctly. (i.e. http://127.0.0.1:8086/what-we-do/ works correctly)

NGINX version 1.13.9
Apache 2.4.33 mpm_prefork
PHP version 7.1

Any thoughts or help getting NGINX to correctly proxy permalinks to apache would be greatly appreciated!

1
  • A shame that no one knows what needs to be done here. I have had this problem multiple times and it makes me wonder if the benefits of reverse proxying Wordpress are worth it. Wonder if I would have the same problems if i used Varnish instead of NGINX. Commented Jun 5, 2018 at 12:47

3 Answers 3

2

Enable or check if mod_rewrite is enabled with this command

sudo a2enmod rewrite

Sign up to request clarification or add additional context in comments.

1 Comment

It is, and like I said, when I access the site as being served by apache the permalinks work. It is only when nginx is proxying that they do not get passed correctly. If I were to remove the nginx proxy and have apache serve directly, then the site 100% works already.
1

I received this same error when I was migrating my Prod environment to Docker, using NGINX, but I was not reverse proxying for Apache. My error was the same, though.

The reason was that I had to change the wp_options to match my new local Port and URL.

SELECT * FROM wp_options WHERE option_name='siteurl' OR option_name='home'; will show you the current URL that your WordPress Config is trying to navigate to. But since you've created a Proxy and now have your WordPress site behind a different port or URL, you might need to change these values.

When you execute that command, you will receive a list of the two URLs that your site is using as your prefix. If it's showing the URL of the Proxy, this probably won't work.

I then modified the URLs to match the location of the NEW backend URL + Port. In your case, you will have to likely change it to match the port and url BEHIND the proxy, not the URL of the proxy itself.

Modifying these values inside my wp-config.php did NOT WORK. e.g.

 define('WP_HOME','http://local.www.greenhousetreatment.com:8080');
 define('WP_SITEURL','http://local.www.greenhousetreatment.com:8080');

THIS DID NOT WORK FOR ME.

I had to manually use the command above in SQL, then UPDATE those values to match both the PORT and the URL of the website. Normally in reverse proxies, you will type in the Proxy URL, it will then hit your service IP, and port. Your service IP and PORT does what it needs to do, as it does not care at all about the proxy. It doesn't even know about the proxy.

Are you sure your wp_options match the actual service URL and PORT, and not the Proxy URL?

I hope this could shed some light.

1 Comment

I just wanted to follow up here, this did not help. The issue was before wordpress even got involved, as you can see above, NGINX was trying to look for an index.php in the wrong location. I'm guessing I probably had to put in some complex rewrite rules in NGINX. I ended up abandoning this because it was taking too long to configure correctly. I have NGINX serving wordpress directly.
1

I'm having the same problem today, and my setup just like yours, what i did to solve this;

modify the nginx config and just add/move the index index.php from server block into location / block.

Here is the example as I comment on the index index.php at server block then add it into location block.

server {
   ...
   root                        /var/www/example.com/live_site;
   access_log                  /var/log/nginx/access-example.com.log main;
   error_log                   /var/log/nginx/error-example.com.log;
   #index                       index.php; ##REMOVE THIS ONE 
   ...
# Wordpress entry point
     location / {
        index index.php index.html ##THEN ADD HERE
        #Try                    file dir    index.php else 404
         try_files               $uri $uri/ /index.php?$args =404;
       ...
}

also don't forgot to check the .htaccess ensure it is for WordPress permalink, or can just go to wordpress setting then save the permalink setting to Post name.

1 Comment

I don't have my setup anymore to test this. But, honestly, if it was something this dead simple I will be upset it took so many years to figure out!! lol

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.