flutterdartnginxnginx-reverse-proxyflutter-http

NGINX reverse proxy returns HTTP 400 with a invalid header line for my flutter app.Works fine in Postman


I've setup Nginx with reverse proxy to my django backend api. The requests from Postman work perfectly fine but on Flutter I get a HTTP 400 157 from the nginx server while performing a GET request.

Upon inspecting the log , I see the following :

client sent invalid header line: "\x3a..." while reading client request headers

Here is my nginx.conf.

worker_processes auto;
events {
    worker_connections 1024;
}

http {

    log_format custom '$remote_addr - $remote_user [$time_local] "$request" '
    '$status $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for" $request_time';

    #Note that these are defined outside of the server block altho they don't necessarily need to be
    proxy_cache_path /tmp/nginx levels=1:2 keys_zone=my_zone:10m inactive=60m;
    proxy_cache_key "$scheme$request_method$host$request_uri";


    default_type application/octet-stream;

    include /etc/nginx/mime.types;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 4096;
    
    
    upstream my-django-rest-api {

        server 172.16.16.1:8080; # The internal Django API service
    }

    server {
        listen 1652;

        # Access & Error Logs
        access_log /var/log/nginx/access.log custom;
        error_log /var/log/nginx/error.log;

        location / {
            proxy_pass http://my-django-rest-api;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            proxy_cache my_zone;
            add_header X-Proxy-Cache $upstream_cache_status;
            proxy_buffering on;
            proxy_cache_valid 200 302 60m;
            proxy_cache_valid 404 1m;

            # WebSocket support (nginx 1.4)
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}

What Do I need to do on the Nginx end before I start troubleshooting it from the Frontend ?

What exactly is "\x3a..." which is termed as Invalid in header line?

Further to the above, I'm adding the headers that are sent for each Postman and flutter for the same GET request:

Postman Headers for a GET request

Key                      Value
Host                     <calculated when request is sent>

User-Agent               PostmanRuntime/7.42

Accept                   */*

Accept Encoding          gzip,deflate,br

Connection               keep-alive

Authorization            Token f384996c63b4****************

Flutter side functions while request is sent

 Future<HttpieResponse> get(url,
      {Map<String, String>? headers,
      Map<String, dynamic>? queryParameters,
      bool? appendLanguageHeader,
      bool? appendAuthorizationToken}) async {
    var finalHeaders = _getHeadersWithConfig(
        headers: headers,
        appendLanguageHeader: appendLanguageHeader,
        appendAuthorizationToken: appendAuthorizationToken);

    if (queryParameters != null && queryParameters.keys.isNotEmpty) {
      url = url + _makeQueryString(queryParameters);
    }

    Response? response;

    try {
      response = await client.get(Uri.parse(url), headers: finalHeaders);
    } catch (error) {
      _handleRequestError(error);
    }

    if (response != null) {
      return HttpieResponse(response);
    }
    throw ();
  }
      
  Map<String, String> _getHeadersWithConfig(
      {Map<String, String>? headers = const {},
      bool? appendLanguageHeader,
      bool? appendAuthorizationToken}) {
    headers = headers ?? {};

    Map<String, String> finalHeaders = Map.from(headers);

    appendLanguageHeader = appendLanguageHeader ?? true;
    appendAuthorizationToken = appendAuthorizationToken ?? false;

    if (appendLanguageHeader) finalHeaders['Accept-Language'] = _getLanguage();

    if (appendAuthorizationToken && authorizationToken != null) {
      finalHeaders['Authorization'] = 'Token $authorizationToken';
    }

    if (magicHeaderName != null && magicHeaderValue != null) {
      finalHeaders[magicHeaderName!] = magicHeaderValue!;
    }


    return finalHeaders;
  }

  String _getLanguage() {
    return _localizationService.getLocale().languageCode.toLowerCase();
  }

.env.json

  {
  "API_URL": "http://24.XX.XX.XX:70/",
  "MAGIC_HEADER_NAME": "",
  "MAGIC_HEADER_VALUE": "",
  }

Solution

  • Presumably the empty magicHeaderName and magicHeaderValue result in the following line being added to the request headers:

    :
    

    Which is not allowed, specifically RFC 9110 defines that the HTTP header name must be non-empty:

    field-name = token
    token = 1*tchar
    

    The 1* means "at least one", so an empty name is not allowed.