First-time poster, so bear with me.

I'm relatively new to Nginx, but have managed to figure out what I've needed... until now.

Nginx v1.0.15 is proxying to PHP-FPM v.5.3.10, which is listening at [Knock on wood] everything has been running smoothly in terms of hosting our CMS and many websites.

Now, we've developed our CMS and configured Nginx such that each supported website has a preview URL (e.g. http://[WebsiteID].ourcms.com/) where the site can be, you guessed it, previewed in those situations where DNS doesn't yet resolve to our server, etc.

Specifically, we use Nginx's Map module (http://wiki.nginx.org/HttpMapModule) and a regular expression in the server_name of the CMS' server{ } block to 1) lookup a website's primary domain name from its preview URL and then 2) forward the request to the "matched" primary domain.

The corresponding Nginx configuration:

map $host $h {
    123.ourcms.com  www.example1.com;
    456.ourcms.com  www.example2.com;
    789.ourcms.com  www.example3.com;


server {
    listen              [OurCMSIPAddress]:80;
    listen              [OurCMSIPAddress]:443 ssl;
    root                /var/www/ourcms.com;
    server_name         ~^(.*)\.ourcms\.com$;
    ssl_certificate     /etc/nginx/conf.d/ourcms.com.chained.crt;
    ssl_certificate_key /etc/nginx/conf.d/ourcms.com.key;

    location / {
        proxy_set_header Host $h;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

(Note: I do realize that the regex in the server_name should be "tighter" for security reasons and match only the format of the website ID (i.e. a UUID in our case).)

This configuration works for 99% of our sites... except those that have a dedicated IP address for an installed SSL certificate. A "502 Bad Gateway" is returned for these and I'm unsure as to why.

This is how I think the current configuration works for any requests that match the regex (e.g. http://123.ourcms.com/):

  1. Nginx looks up the website's primary domain from the mapping, and
  2. as a result of the proxy_pass directive, passes the request back to Nginx itself, which
  3. since the proxied request has a hostname corresponding to the website's primary domain name, via the proxy_set_header Host $h directive, Nginx handles the request as if it was as direct request for that hostname.

Please correct me if I'm wrong in this understanding.

Should I be proxying to those website's dedicated IP addresses? I tried this, but it didn't seem to work? Is there a setting in the Proxy module that I'm missing?

Thanks for the help.


  • In case this helps, we drop connections without a matching hostname, which I understand is a best practice, with the following: server { listen 80 default; server_name _; return 444; } – Matthew Borgman Jun 2 '12 at 16:38
  • What do the debug logs say is happening? – womble Jun 2 '12 at 17:51
  • The error log produces the following when trying to access a site via its preview URL: upstream prematurely closed connection while reading response header from upstream, client:, server: ~^(.*)\.ourcms\.com$, request: "GET /index.html HTTP/1.1", upstream: "", host: "12345.ourcms.com" – Matthew Borgman Jun 2 '12 at 23:05
  • FYI, "index.html.html" in the above error was a typo from when I edited out an overly-long URL. – Matthew Borgman Jun 2 '12 at 23:16
  • Add useful information to your question, not as a comment. Also, that isn't a debug log entry. – womble Jun 3 '12 at 9:53

except those that have a dedicated IP address for an installed SSL certificate.

It looks to me like you are listening on only one IP address. You will also need 'listen' directives for the additional IPs for the SSL domains. Unless you have an wildcard SSL certificate, I would think that you would need one server{} definition for each of these hosts.

Here's a related pattern that I use that might be helpful if you are trying to avoid the need to maintain your the configs for your SSL Nginx domains completely by hand:

Part of my Nginx configuration is stored under source control as a template. The "init" script for Nginx has been modified so that every time Nginx is reloaded or restarted, a Perl script is scalled that fills in the template variables depending on the environment (alpha, beta, production, etc). A new plain text file is written out and Nginx includes the "rendered" template as an include file.

This allows for new possibilities for automation and DRY. Perhaps it would help in your situation.

  • 1
    Thanks for your response, Mark. Each website does have its own server block that either a) listens on the shared IP address for non-SSL enabled sites or b) listens on ports 80 and 443 of dedicated IP address for that single website. As for generating the configurations, we do something similar because we're hosted 100+ websites off the single CMS installation. To simplify things, the CMS takes configuration templates and customizes them per website when its first setup/modified. – Matthew Borgman Jun 2 '12 at 22:52

For anyone that may stumble across this, the solutions was the following Nginx configuration:

# Map preview URLs to shared or dedicated IP addresses
map $host $i {
        default         [IP for Shared VHosts];

        123.ourcms.com  [Dedicated IP Address 1];   # www.example1.com;
        456.ourcms.com  [Dedicated IP Address 2];   # www.example2.com;
        789.ourcms.com  [Dedicated IP Address 3];   # www.example3.com;

# Map preview URLS to appropriate hostname
map $host $h {

        123.ourcms.com   www.example1.com;
        456.ourcms.com   www.example2.com;
        789.ourcms.com   www.example3.com;


# Configuration for "preview" server 
server {
    listen                  [OurCMSIPAddress]:80;
    listen                  [OurCMSIPAddress]:443 ssl;
    error_log               /var/log/nginx/error-ourcms.com.log debug;
    root                    /var/www/ourcms.com;
    server_name             ~^(.*)\.ourcms\.com$;
    ssl_certificate         /etc/nginx/conf.d/ourcms.com.chained.crt;
    ssl_certificate_key     /etc/nginx/conf.d/ourcms.com.key;

    location / {
        proxy_pass http://$i;
        proxy_set_header Host $h;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

The trick, and I think @MarkStosberg was alluding to this and I didn't understand, was to lookup both hostname and IP address for the previewed website. Further, the server blocks for each virtual host had to contain listen [SharedIPForVhosts]:80 and not just listen *:80.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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