Skip to content

Web Vitals proxy returns 500 when ingest endpoint is unreachable (e.g. ETIMEDOUT) #35

@Guistoff081

Description

@Guistoff081

Summary

When using withBetterStack() and <BetterStackWebVitals />, requests to POST /_betterstack/web-vitals are rewritten to the Better Stack ingest URL. If that external request fails (e.g. network timeout, ingest down, or firewall), Next.js surfaces the error to the client as 500 Internal Server Error. This affects development and any environment where the ingest endpoint is slow or unreachable.

Environment

  • Package: @logtail/next v0.3.1
  • Next.js: 16.x (Turbopack) With proxy.ts
  • Node: 22+

Steps to reproduce

  1. Configure Better Stack with valid NEXT_PUBLIC_BETTER_STACK_SOURCE_TOKEN and NEXT_PUBLIC_BETTER_STACK_INGESTING_URL (e.g. https://sXXXX.eu-nbg-2.betterstackdata.com).
  2. Use withBetterStack(nextConfig) in next.config.ts and render <BetterStackWebVitals /> in the root layout.
  3. Simulate an unreachable ingest (e.g. block the domain, use an invalid URL, or rely on a slow/unstable network).
  4. Load the app and let the client send web vitals to /_betterstack/web-vitals.

Expected behavior

  • The client receives a 2xx response (e.g. 204 No Content) when the ingest is unreachable, so that:
    • The app does not show failed network requests or 500s in the console.
    • Telemetry failure does not impact user-facing behavior.
  • Optionally: the proxy could use a timeout (e.g. 5s) and return 204 on timeout/error instead of propagating the failure.

Actual behavior

  • The proxy request to the ingest URL fails (e.g. ETIMEDOUT).
  • Next.js returns 500 Internal Server Error to the client for POST /_betterstack/web-vitals.
  • Browser console shows: POST http://localhost:3000/_betterstack/web-vitals 500 (Internal Server Error).
  • Server logs show: Failed to proxy https://sXXXX.eu-nbg-2.betterstackdata.com/ AggregateError: { code: 'ETIMEDOUT' }.

Possible fix

In the rewrite/proxy layer that handles /_betterstack/web-vitals (and optionally /_betterstack/logs):

  • Forward the request to the ingest URL with a timeout (e.g. 5s).
  • On timeout or network error, return 204 No Content (or another 2xx) instead of 500, so the client does not see a failure.
  • Optionally log the error server-side for debugging.

Workaround

We added a custom Route Handler at app/_betterstack/web-vitals/route.ts that runs before the config rewrite. It forwards the POST to the ingest URL with an AbortController timeout (5s) and returns 204 on any error. That way the client never receives 500 when the ingest is unreachable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions