title: nginx reverse proxy to wordpress docker container hangs with HTTP 301 error
body: <p>I’m trying to use nginx as a reverse proxy for multiple docker containers running wordpress.  The nginx instance and docker are running on Ubuntu 16.04.3 server.  I have been testing this out on my local dev environment with Vagrant using a separate Ubuntu box for nginx and another for docker.  The vagrant configuration works as expected, but when I try to make a similar configuration on a single physical Ubuntu server the route to the wordpress docker container hangs and eventually returns an HTTP 301 error.</p>
  
  <p>Note: Using a similar nginx reverse proxy configuration for other docker containers listening on different port works.  For example, running Jenkins in docker and using a reverse proxy to that container works successfully.  </p>
  
  <p>Here are the configurations I am using with Vagrant and then on my physical Ubuntu server:</p>
  
  <p>Working solution with Vagrant and Two separate Ubuntu boxes</p>
  
  <p><strong>Vagrant Configuration</strong></p>
  
  <p>Nginx running in a separate Ubuntu Box</p>
  
  <p>Setting local host file
  /etc/hosts  10.10.45.10 - wp.dev</p>
  
  <p><strong>nginx configuration</strong></p>
  
  <pre><code>server {
  listen 80;
  listen [::]:80;
  server_name wp.dev;
  error_log /var/log/nginx/wp_dev_error.log debug;
  
      location / {
          proxy_pass      http://10.10.45.11:8080;
          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;
      }
  }
  </code></pre>
  
  <p><strong><em>Wordpress docker configuration</em></strong></p>
  
  <p><strong>Docker-compose.yml file:</strong></p>
  
  <pre><code>  version: "2"
    services:
    my-wpdb:
      image: mariadb
      ports:
        - "8081:3306"
      environment:
        MYSQL_ROOT_PASSWORD: &lt;some_password&gt;
    my-wp:
      image: wordpress
      volumes:
        - ./:/var/www/html
      ports:
        - "8080:80"
      links:
        - my-wpdb:mysql
      environment:
        WORDPRESS_DB_PASSWORD: &lt;some_password&gt;
  </code></pre>
  
  <p><strong>Run docker container</strong></p>
  
  <p>docker-compose up -d</p>
  
  <p><strong>Route</strong></p>
  
  <p>wp.dev (10.10.45.10) →  docker_wp (10.10.45.11 port 8080)</p>
  
  <p>Curl test:  curl wp.dev  --  SUCCESS
  10.10.45.1 - - [18/Aug/2017:21:38:37 +0000] "GET / HTTP/1.1" 200 51638 "-" "curl/7.54.0"</p>
  
  <p><strong>Broken Configuration</strong></p>
  
  <p>/etc/nginx/sites-available/sub1.mydomain.com.conf</p>
  
  <pre><code>server {
      listen 80;
      listen [::]:80;
      server_name sub1.mydomain.com;
      error_log /var/log/nginx/mydomain_nonssl_error.log debug;
  
      location / {
  
          proxy_pass http://localhost:8080;
          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;
  
      }
  }
  </code></pre>
  
  <p>Docker-compose file for wordpress docker container is the same as the vagrant configuration above.</p>
  
  <p><strong>Testing configuration and results</strong></p>
  
  <p>Curl test:  curl  ..com</p>
  
  <pre><code>159.203.127.57 - - [18/Aug/2017:15:37:52 -0600] "GET / HTTP/1.1" 301 0 "-" "curl/7.47.0"
  </code></pre>
  
  <p>The page hangs and I see in the header a HTTP 301 error.  </p>
  
  <p>curl -v http://</p>
  
  <pre><code>* Rebuilt URL to: http://&lt;my_testdomain&gt;/
  *   Trying xx.xx.xx.91...
  * Connected to sub1.mydomain.com (xx.xx.xx.91) port 80 (#0)
  &gt; GET / HTTP/1.1
  &gt; Host: sub1.mydomain.com
  &gt; User-Agent: curl/7.47.0
  &gt; Accept: */*
  &gt; 
  &lt; HTTP/1.1 301 Moved Permanently
  &lt; Server: nginx/1.10.3 (Ubuntu)
  &lt; Date: Sat, 19 Aug 2017 15:05:38 GMT
  &lt; Content-Type: text/html; charset=UTF-8
  &lt; Content-Length: 0
  &lt; Connection: keep-alive
  &lt; X-Powered-By: PHP/5.6.31
  **&lt; Location: http://&lt;my_test_domain&gt;:8080/**
  &lt; 
  * Connection #0 to host &lt;my_test_domain&gt; left intact
  </code></pre>
  
  <p>Note: that the route continues to use the destination port in the URL.  I don’t see this in my Vagrant configuration.  This may be the problem.  </p>
  
  <p>I’ve tried different nginx configuration to hide the destination port but nothing seems to work.  </p>
  
  <p>Here are some specific questions that may help troubleshoot this problem:</p>
  
  <ol>
  <li>How can I enable more debug information with nginx.  I’m using the following “error_log” setting but would like to enable “verbose” logging to determine how routing is working.</li>
  </ol>
  
  <p>error_log /var/log/nginx/mydomain_nonssl_error.log debug;</p>
  
  <ol start="2">
  <li><p>Why is the destination port still showing in the URL when I run nginx reverse proxy on the same machine, but a similar configuration in Vagrant running separate boxes hides the port and uses the original URL in the request?</p></li>
  <li><p>Could the problem be in the Docker wordpress/Apache container that is causing the 301 HTTP error?</p></li>
  </ol>
  
  <p>I’ve been working on this problem for several days and have not been able to resolve the issue.  Thanks in advance for your help.</p>
  
  <p>**Nginx Log file of reverse proxy **</p>
  
  <pre><code>    2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: "X-Real-IP: "
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script var: "168.179.61.161"
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: "
  "
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: "X-Forwarded-For: "
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script var: "168.179.61.161"
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: "
  "
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: "Connection: close
  "
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: ""
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: ""
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: ""
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http script copy: ""
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http proxy header: "user-agent: curl/7.53.0"
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http proxy header: "accept: */*"
  2017/08/28 09:53:14 [debug] 11853#11853: *1 http proxy header:
  "GET / HTTP/1.0
  Host: &lt;mydevsite&gt;
  X-Real-IP: 168.179.61.161
  X-Forwarded-For: 168.179.61.161
  Connection: close
  user-agent: curl/7.53.0
  accept: */*
  
  … snip ….
  
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http upstream request: "/?"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http upstream process header
  2017/08/28 09:53:15 [debug] 11853#11853: *1 malloc: 000055D755D248F0:4096
  2017/08/28 09:53:15 [debug] 11853#11853: *1 recv: fd:29 246 of 4096
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy status 301 "301 Moved Permanently"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 posix_memalign: 000055D755D168A0:4096 @16
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy header: "Date: Mon, 28 Aug 2017 15:53:14 GMT"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy header: "Server: Apache/2.4.10 (Debian)"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy header: "X-Powered-By: PHP/5.6.31"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy header: "Location: http://&lt;mydevsite&gt;:8080/"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy header: "Content-Length: 0"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy header: "Connection: close"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy header: "Content-Type: text/html; charset=UTF-8"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy header done
  2017/08/28 09:53:15 [debug] 11853#11853: *1 xslt filter header
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 header filter
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: ":status: 301"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "server: nginx/1.10.3 (Ubuntu)"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "date: Mon, 28 Aug 2017 15:53:15 GMT"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "content-type: text/html; charset=UTF-8"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "content-length: 0"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "location: http://&lt;mydevsite&gt;:8080/"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "x-powered-by: PHP/5.6.31"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "strict-transport-security: max-age=63072000; includeSubdomains"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "x-frame-options: DENY"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 output header: "x-content-type-options: nosniff"
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2:1 create HEADERS frame 000055D755D16B78: len:200
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http cleanup add: 000055D755D16C60
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 frame out: 000055D755D16B78 sid:1 bl:1 len:200
  2017/08/28 09:53:15 [debug] 11853#11853: *1 SSL buf copy: 9
  2017/08/28 09:53:15 [debug] 11853#11853: *1 SSL buf copy: 200
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2:1 HEADERS frame 000055D755D16B78 was sent
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http2 frame sent: 000055D755D16B78 sid:1 bl:1 len:200
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http cacheable: 0
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http proxy filter init s:301 h:0 c:0 l:0
  2017/08/28 09:53:15 [debug] 11853#11853: *1 http upstream process upstream
  2017/08/28 09:53:15 [debug] 11853#11853: *1 pipe read upstream: 1
  2017/08/28 09:53:15 [debug] 11853#11853: *1 pipe preread: 0
  2017/08/28 09:53:15 [debug] 11853#11853: *1 readv: 1, last:3850
  2017/08/28 09:53:15 [debug] 11853#11853: *1 pipe recv chain: 0
  2017/08/28 09:53:15 [debug] 11853#11853: *1 pipe buf free s:0 t:1 f:0 000055D755D248F0, pos 000055D755D249E6, size: 0 file: 0, size: 0
  </code></pre>