Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nginx Reverse Proxy to Mercure Hub - 502 Bad Gateway Error [Symfony 6 - API Platform] #874

Open
sayou opened this issue Feb 21, 2024 · 3 comments

Comments

@sayou
Copy link

sayou commented Feb 21, 2024

Hello,

Firstly, I want to express my gratitude for the Mercure Hub. It's been a vital part of my web development work.

I am encountering an issue with setting up an Nginx reverse proxy for the Mercure hub on my VPS (not using Docker). Despite following the configuration guides, I'm persistently facing a "502 Bad Gateway" error.

Here's a breakdown of my setup and the problem:

  • Environment: VPS with both Nginx and the Mercure hub installed.
  • SSL Setup: I'm using Let's Encrypt SSL certificates for both Nginx and Mercure.
  • Nginx Configuration: Configured as a reverse proxy to the Mercure hub.
  • Error Encountered: Consistent SSL handshake issues, leading to a "502 Bad Gateway" error. The error log shows SSL_do_handshake() failed (SSL: error:0A000438:SSL routines::tlsv1 alert internal error:SSL alert number 80).

I've attached my Nginx configuration and the relevant logs for your reference. Any insights or suggestions to resolve this would be immensely helpful.

Thank you for your time and support.

vHost code:

server {
  listen 80;
  listen [::]:80;
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  {{ssl_certificate_key}}
  {{ssl_certificate}}
  server_name test.test.com;
  {{root}}

  {{nginx_access_log}}
  {{nginx_error_log}}

  if ($scheme != "https") {
    rewrite ^ https://$host$uri permanent;
  }

  {{settings}}

  location / {
    {{varnish_proxy_pass}}
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_hide_header X-Varnish;
    proxy_redirect off;
    proxy_max_temp_file_size 0;
    proxy_connect_timeout      720;
    proxy_send_timeout         720;
    proxy_read_timeout         720;
    proxy_buffer_size          128k;
    proxy_buffers              4 256k;
    proxy_busy_buffers_size    256k;
    proxy_temp_file_write_size 256k;
  }
  
  location /.well-known/mercure {
    proxy_pass https://127.0.0.1:8081/.well-known/mercure;
    proxy_read_timeout 24h;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_ssl_server_name on;

    ## Be sure to set USE_FORWARDED_HEADERS=1 to allow the hub to use those headers ##
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  location ~* ^.+\.(css|js|jpg|jpeg|gif|png|ico|gz|svg|svgz|ttf|otf|woff|woff2|eot|mp4|ogg|ogv|webm|webp|zip|swf|map)$ {
    add_header Access-Control-Allow-Origin "*";
    expires max;
    access_log off;
  }

  if (-f $request_filename) {
    break;
  }
}

server {
  listen 8080;
  listen [::]:8080;
  server_name test.test.com;
  {{root}}

  try_files $uri $uri/ /index.php?$args;
  index index.php index.html;

  location ~ \.php$ {
    include fastcgi_params;
    fastcgi_intercept_errors on;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    try_files $uri =404;
    fastcgi_read_timeout 3600;
    fastcgi_send_timeout 3600;
    fastcgi_param HTTPS "on";
    fastcgi_param SERVER_PORT 443;
    fastcgi_pass 127.0.0.1:{{php_fpm_port}};
    fastcgi_param PHP_VALUE "{{php_settings}}";
  }

  if (-f $request_filename) {
    break;
  }
}

Caddyfile code:

{
    order mercure after encode
    http_port 8081
    https_port 8082
    {§GLOBAL_OPTIONS}
}
{ $CADDY_EXTRA_CONFIG}
{$SERVER_NAME: localhost} {
    log {
    format filter {
        wrap console
            fields {
                uri query {
                    replace authorization REDACTED
                }
            }
        }
    }
    encode zstd gzip
    mercure {
        tls /etc/nginx/ssl-certificates/test.test.com.crt /etc/nginx/ssl-certificates/test.test.com.key
        transport_url {$MERCURE_TRANSPORT_URL:bolt://mercure.db}
        publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env .MERCURE_PUBLISHER_JWT_ALG}
        subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env. MERCURE_SUBSCRIBER_JWT_ALG}
        cors_origins *
        publish_origins *
        demo
        anonymous
        subscriptions
        # Extra directives
        {SMERCURE_EXTRA_DIRECTIVES}
    }
    {$CADDY_SERVER_EXTRA_DIRECTIVES}
    redir / /.well-known/mercure/ui/
    respond /healthz 200
    respond /robots.txt `User-agent: *
Disallow: /`
    respond "Not Found" 404
}

my .env:

# The URL of the Mercure hub, used by the app to publish updates (can be a local URL)
MERCURE_URL=https://localhost:8082/.well-known/mercure
# The public URL of the Mercure hub, used by the browser to connect
MERCURE_PUBLIC_URL=https://localhost/.well-known/mercure
# The secret used to sign the JWTs
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"

realtime.html:

<!DOCTYPE html>
<html>
<head>
    <title>Mercure Real-Time Updates Test</title>
</head>
<body>
    <h1>Real-Time Updates</h1>
    <div id="update-container">
        <!-- Real-time updates will be displayed here -->
    </div>

    <script>
        // Replace with your actual Mercure hub URL
        const mercureHubURL = 'https://test.test.com/.well-known/mercure';

        // Replace with the actual topic URL for the resource you want to track updates for
        const topicURL = 'https://test.test.com/api/games/2121855095-uefa-nations-league-2022-2023-netherlands-vs-croatia';

        const eventSource = new EventSource(`${mercureHubURL}?topic=${topicURL}`);
        
        eventSource.onmessage = (event) => {
            // Handle the real-time update received from Mercure
            const updateData = JSON.parse(event.data);
            
            // Display the real-time update in the container
            const updateContainer = document.getElementById('update-container');
            updateContainer.innerHTML += `<p>Real-time update: ${JSON.stringify(updateData)}</p>`;
            
            // You can update your UI or perform any necessary actions with the updated data here
        };
    </script>
</body>
</html>

Best regards,

PS: I use Symfony 6 & API platform

@sayou
Copy link
Author

sayou commented Feb 21, 2024

UPDATE:

after running Mercure using :

MERCURE_PUBLISHER_JWT_KEY='!ChangeThisMercureHubJWTSecretKey!' MERCURE_SUBSCRIBER_JWT_KEY='!ChangeThisMercureHubJWTSecretKey!' SERVER_NAME=https://localhost ./mercure run

I got a little close to solving the problem, but it started giving me this error:

EventSource's response has a MIME type ("text/plain") that is not "text/event-stream". Aborting the connection.

@dunglas
Copy link
Owner

dunglas commented Feb 22, 2024

Thanks for the kind words!

Wouldn't it be simpler to use plain HTTP between NGINX and the hub? If they are on the same machine, there is no benefit in encrypting the inner traffic.

The MIME type error is weird, could you copy the full request and response (including headers) please?

@sayou
Copy link
Author

sayou commented Feb 26, 2024

The problem has been resolved by adopting measures discussed earlier (#854).

I think that the documentation needs a more detailed and thorough explanation on the official website.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants