Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wordpress Permalinks return 404 when using NGINX as a reverse proxy to apache

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!

like image 388
Joseph Michael Avatar asked May 23 '18 15:05

Joseph Michael


2 Answers

Enable or check if mod_rewrite is enabled with this command

sudo a2enmod rewrite

like image 106
Gung Avatar answered Nov 09 '22 22:11

Gung


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.

like image 1
luminol Avatar answered Nov 09 '22 21:11

luminol