Encited

Setup with Netlify Edge Functions

How it works#

Your edge middleware/worker calls /api/prerender/render?url=... for HTML document requests. If Encited returns 200 you serve rendered HTML. If it returns 304 with a Location header, prerendering didn't apply — fall back to your origin SPA.

Prerequisites#

  • An Encited account and API key (Settings → API Keys)
  • A domain added + verified in your Encited dashboard
  • A Netlify site serving your SPA

Setup#

Set LOVABLEHTML_API_KEY in Netlify with the Functions scope. The Edge Function reads it through Netlify.env; don't paste it into source control.

1. Create the Edge Function file#

At netlify/edge-functions/lovablehtml.js

encited

// netlify/edge-functions/lovablehtml.js (Netlify Edge Function)
export default async (request, context) => {
  // Only handle public GET navigations.
  // Treat missing/empty Accept and bare '*/*' as HTML so crawler tests
  // (curl without -H, default fetch) still route through prerender.
  // Asset requests from browsers send specific Accept (e.g. 'text/css,*/*;q=0.1')
  // so they won't match.
  const accept = (request.headers.get('accept') || '').trim();
  const isHtmlRequest = !accept || accept === '*/*' || accept.includes('text/html');
  if (request.method !== 'GET' || !isHtmlRequest) return context.next();

  const headers = {
    // Set LOVABLEHTML_API_KEY in your Netlify site's environment variables (Functions scope).
    'x-lovablehtml-api-key': Netlify.env.get('LOVABLEHTML_API_KEY'),
    accept: 'text/html',
    'accept-language': request.headers.get('accept-language') || '',
    'sec-fetch-mode': request.headers.get('sec-fetch-mode') || '',
    'sec-fetch-site': request.headers.get('sec-fetch-site') || '',
    'sec-fetch-dest': request.headers.get('sec-fetch-dest') || '',
    'sec-fetch-user': request.headers.get('sec-fetch-user') || '',
    'upgrade-insecure-requests': request.headers.get('upgrade-insecure-requests') || '',
    referer: request.headers.get('referer') || '',
    'user-agent': request.headers.get('user-agent') || '',
  };

  try {
    const r = await fetch(
      'https://encited.com/api/prerender/render?url=' + encodeURIComponent(request.url),
      { headers, redirect: 'manual' },
    );

    // 301 = configured redirect rule matched - forward to client
    if (r.status === 301) {
      const loc = r.headers.get('location');
      if (loc) {
        return new Response(null, {
          status: 301,
          headers: { location: loc, 'cache-control': 'no-store' },
        });
      }
    }

    // 304 = not pre-rendered, pass through to origin
    if (r.status === 304) {
      return context.next();
    }

    if (r.status === 200 && (r.headers.get('content-type') || '').includes('text/html')) {
      const responseHeaders = new Headers(r.headers);
      for (const name of ['content-encoding', 'content-length', 'transfer-encoding', 'connection', 'keep-alive']) {
        responseHeaders.delete(name);
      }
      responseHeaders.set('content-type', 'text/html; charset=utf-8');
      return new Response(r.body, { status: 200, headers: responseHeaders });
    }
  } catch {
    // Prerender unreachable → continue to the existing Netlify request chain
  }

  return context.next();
};

export const config = {
  path: "/*",
  onError: "bypass",
};

2. Deploy to Netlify#

The edge function runs on every request and lets Encited classify public HTML GET requests.

How to Test#

  1. Call the render API directly and verify x-lovablehtml-render-cache: hit | miss
  2. Hit your site with Accept: text/html and verify you get HTML back
  3. Verify a normal browser request falls through to your existing origin
# 1) Call the Encited render API directly
curl -sS -D - -o /dev/null \
  -H "x-lovablehtml-api-key: <API_KEY>" \
  -H "Accept: text/html" \
  "https://encited.com/api/prerender/render?url=https%3A%2F%2Fyour-domain.com%2Fyour-page"

# Look for:
# - HTTP/1.1 200
# - x-lovablehtml-render-cache: hit | miss
# - x-lovablehtml-snapshot-key: ...
# 2) Hit your site with an HTML Accept header
curl -sS -D - -o /dev/null \
  -H "Accept: text/html" \
  -A "Googlebot" \
  "https://your-domain.com/your-page"

# Look for:
# - HTTP/1.1 200
# - content-type: text/html
# 3) Browser passthrough (Encited returns 304; the Edge Function calls context.next())
curl -sS -D - -o /dev/null \
  -A "Mozilla/5.0" \
  "https://your-domain.com/your-page"

If you run bot protection or rate limiting in front of this site, set a render token and check it in your edge function so renders and audit crawls get through. See Netlify.

Best Practices#

  • Keep secrets out of git — Store keys in your platform's secret manager or env vars; rotate/revoke when compromised.
  • Don't proxy static assets — Only call Encited for HTML document requests. Always pass through JS/CSS/images/fonts.
  • Handle 304 passthrough — 304 means prerendering doesn't apply. Fall back to your origin and use the Location header as the target URL when needed.
  • Invalidate after content changes — Use the cache invalidation endpoints (optionally with prewarm) after deploys or CMS updates.

Need help? Check the full API reference for prerender, cache, and analytics endpoint docs, or jump directly to Analytics API, or contact us if you run into issues.