nginx proxy_pass receive content/status headers when http 4xx

Brian asked:

Was having some trouble as chronicled here – http://stackoverflow.com/questions/22570550/play-2-2-1-simpleresult-4xx-response-body-possible-via-cors-xhr

But I didn’t focus on nginx since the 200 OK happy path was working as expected. It was 400 level http errors that weren’t working as expected. I wasn’t receiving a body or able to get the status code inside browser XHR objects.

4xx http errors worked as expected via XHR (readyState=3 experienced) only when I was passing Access-Control-Allow-Origin from the application server, but I don’t want this embedded in the code.

Is the only way to pass content and status headers for 4xx responses is to compile with HeadersMoreNginxModule? Not ideal, but doable.

package controllers

import play.api._
import play.api.mvc._
import play.api.libs.iteratee.Enumerator

object Application extends Controller {

  def index = Action {
    Ok(views.html.index("Your new application is ready."))
  }

  def goodResponse = Action { implicit request =>
    // Ok("Hello world!")
    SimpleResult(
      header = ResponseHeader(200, Map(CONTENT_TYPE -> "application/json")),
      body = Enumerator("Hello world!".getBytes())
    )
  }

  def badResponse = Action { implicit request =>
    // BadRequest(
    //   """{"error":"bad request"}"""
    // )
    SimpleResult(
      header = ResponseHeader(
        400, 
        Map(
          CONTENT_TYPE -> "application/json",
          ACCESS_CONTROL_ALLOW_ORIGIN -> "*"     // FIXME: move to nginx?
        )
      ),
      body = Enumerator("""{"error":"bad request"}""".getBytes())
    )
  }

}

My nginx.conf uses a proxy_pass to the play server on port 9000:

http {

  # ...snipped...

  upstream api {
    server localhost:9000;
  }

  server {
    #listen   80; ## listen for ipv4; this line is default and implied
    #listen   [::]:80 default ipv6only=on; ## listen for ipv6
    listen 80;
    listen 443 default_server ssl;
    ssl_certificate      /usr/local/etc/nginx/ssl/api.crt;
    ssl_certificate_key  /usr/local/etc/nginx/ssl/nginx.key;

    root /usr/share/nginx/www;
    index index.html index.htm;

    # Make site accessible from https://api/
    server_name api;

    location / {
      # See http://enable-cors.org/server_nginx.html
      # Modified to set 'Access-Control-Allow-Origin' "$http_origin"
      include nginx_cors.conf;

      proxy_pass http://api;

    }

  }
}

My answer:


Check your entire nginx configuration and make sure you didn’t set proxy_intercept_errors on; somewhere. This setting should be off in your scenario.


View the full question and answer on Server Fault.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.