Servir un centro de ayuda con recursos estáticos

CloudflareBeginner
Practicar Ahora

Introducción

Un centro de ayuda necesita páginas públicas rápidas, un endpoint JSON de estado y un documento exclusivo para el personal. Un archivo estático puede recibir accidentalmente prioridad sobre el código encargado de gestionar una solicitud. Observará este comportamiento de forma local, configurará el enrutamiento con prioridad para el Worker y desplegará una política explícita que mantenga útil el sitio público mientras protege un recurso sintético del personal.

Este laboratorio independiente comienza en /home/labex/project/help-center con Node.js 22.22.0, Wrangler 4.131.1 instalado localmente en el proyecto y recursos HTML/CSS/JavaScript proporcionados. Use su propia cuenta de aprendizaje de Cloudflare y los conocimientos sobre autorización, despliegue y archivos de secretos aprendidos anteriormente. No necesita una VM anterior, recursos en la nube, un dominio comprado, una base de datos ni una actualización de pago. Las solicitudes cuentan para el uso normal de la cuenta.

Todo el contenido y las credenciales son sintéticos. Mantenga un terminal abierto. La configuración insegura inicial permanecerá local; solo se desplegará el Worker corregido. Elimine el despliegue, borre la credencial de prueba local y cierre la sesión antes de finalizar la VM.

Observar localmente el enrutamiento con prioridad para los recursos

En este paso, inspeccionará la estructura proporcionada del centro de ayuda y observará cómo, de forma predeterminada, los archivos coincidentes reciben prioridad sobre un Worker. Los recursos incluyen deliberadamente un archivo /api/health en conflicto y un manual ficticio del personal. Todo es sintético y esta primera configuración permanecerá local.

cd /home/labex/project/help-center
node --version
npx wrangler --version
ls -R public

Espere ver Node v22.22.0 y Wrangler 4.131.1. La configuración instaló herramientas locales del proyecto; para reproducirla en otro lugar, use npm ci con el archivo de bloqueo del proyecto. El directorio público contiene HTML, CSS, JavaScript del navegador y los dos recursos de prueba del enrutamiento. No coloque credenciales ni documentos internos reales allí.

WORKER_NAME="labex-help-$(openssl rand -hex 6)"
cat > wrangler.jsonc <<CONFIG
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-09-14",
  "workers_dev": true,
  "preview_urls": false,
  "assets": {
    "directory": "./public",
    "binding": "ASSETS",
    "html_handling": "none",
    "not_found_handling": "none",
    "run_worker_first": false
  }
}
CONFIG

directory selecciona los archivos que se cargarán, mientras que binding los expone al controlador como env.ASSETS. html_handling: none conserva las rutas de archivo explícitas; not_found_handling: none evita una redirección automática de SPA. El controlador asigna explícitamente / a /index.html porque el manejo automático de HTML está desactivado. Su intención es devolver JSON para el estado y delegar las demás rutas al almacén de recursos.

cat > src/index.js <<'JS'
export default {
  async fetch(request, env) {
    if (new URL(request.url).pathname === '/api/health') {
      return Response.json({status: 'ok', service: 'help-center'});
    }
    const assetUrl = new URL(request.url);
    if (assetUrl.pathname === '/') assetUrl.pathname = '/index.html';
    return env.ASSETS.fetch(new Request(assetUrl, request));
  }
};
JS
npx wrangler dev --ip 0.0.0.0 --port 8080 > dev.log 2>&1 &
cat dev.log

Espere a que el registro indique que el servidor está listo antes de continuar; repita cat si es necesario.

curl -i http://127.0.0.1:8080/
curl -i http://127.0.0.1:8080/styles.css
curl -i http://127.0.0.1:8080/api/health
curl -i http://127.0.0.1:8080/staff/handbook.html

La página de inicio y el CSS devuelven 200. Health devuelve el texto estático STATIC_HEALTH_PLACEHOLDER, no el JSON del controlador, porque el recurso coincidente recibe prioridad. El manual sintético también se puede leer directamente. Esto demuestra la precedencia del enrutamiento, no un despliegue seguro. No despliegue esta configuración inicial. Use la verificación antes de modificarla.

Si su laboratorio ofrece una vista previa web en el puerto 8080, ábrala ahora. La estructura del centro de ayuda se cargará, pero su línea de estado indicará que el estado de la API no está disponible porque el navegador esperaba JSON. Considere las respuestas de la CLI como comprobaciones de enrutamiento autorizadas; la vista previa es un punto de comprobación visual.

El ejemplo siguiente muestra el problema inicial: la página y la hoja de estilos se cargan, pero API status unavailable significa que el navegador no recibió el JSON de estado esperado. Que la estructura de la página funcione por sí sola no confirma que el enrutamiento de la API funcione.

Centro de ayuda antes de corregir el enrutamiento, con API status unavailable

Ejecutar el Worker antes que los recursos y proteger el contenido del personal

En este paso, hará que el controlador se ejecute antes de cualquier coincidencia estática. Detenga el proceso de desarrollo real que se muestra mediante jobs; el ejemplo supone que es el trabajo 1.

jobs
kill %1
cat > wrangler.jsonc <<CONFIG
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-09-14",
  "workers_dev": true,
  "preview_urls": false,
  "assets": {
    "directory": "./public",
    "binding": "ASSETS",
    "html_handling": "none",
    "not_found_handling": "none",
    "run_worker_first": true
  }
}
CONFIG

Con run_worker_first: true, todas las solicitudes entran en el controlador, incluidos los archivos que de otro modo coincidirían directamente. También existen patrones de ruta selectivos, pero esta aplicación pequeña utiliza una única política de enrutamiento explícita. Consulte Static Assets configuration.

Genere una credencial desechable para el personal mediante el flujo de trabajo con archivos de secretos aprendido anteriormente. umask restringe los permisos de los archivos nuevos. Este es un token de portador exclusivo del laboratorio, nunca un token de API de cuenta. Manténgalo fuera de los archivos públicos, del JavaScript del navegador, de las URL y de los registros.

umask 077
STAFF_TOKEN=$(openssl rand -hex 24)
printf 'STAFF_TOKEN=%s\n' "$STAFF_TOKEN" > .dev.vars
cat .gitignore

Confirme que .dev.vars* y .env* estén ignorados. Sustituya el controlador por la política completa siguiente. Esta política decodifica la ruta una sola vez, sirve el estado como JSON, permite únicamente los archivos públicos indicados, comprueba la credencial del personal antes de obtener ese recurso y rechaza las rutas desconocidas. La solicitud enviada a ASSETS no contiene el encabezado Authorization del cliente. Las respuestas protegidas usan private, no-store.

cat > src/index.js <<'JS'
export default {
  async fetch(request, env) {
    const url = new URL(request.url);
    let path;
    try { path = decodeURIComponent(url.pathname); }
    catch { return Response.json({error: 'not_found'}, {status: 404}); }
    if (path === '/api/health') {
      if (request.method !== 'GET') {
        return Response.json({error: 'method_not_allowed'}, {status: 405, headers: {Allow: 'GET'}});
      }
      return Response.json({status: 'ok', service: 'help-center'});
    }
    const publicPaths = ['/', '/index.html', '/styles.css', '/app.js'];
    if (path === '/staff/handbook.html') {
      if (!env.STAFF_TOKEN) return Response.json({error: 'staff_unconfigured'}, {status: 503});
      if (request.headers.get('Authorization') !== `Bearer ${env.STAFF_TOKEN}`) {
        return Response.json({error: 'unauthorized'}, {status: 401});
      }
    } else if (!publicPaths.includes(path)) {
      return Response.json({error: 'not_found'}, {status: 404});
    }
    if (!['GET', 'HEAD'].includes(request.method)) {
      return Response.json({error: 'method_not_allowed'}, {status: 405, headers: {Allow: 'GET, HEAD'}});
    }
    url.pathname = path === '/' ? '/index.html' : path;
    // Only known paths reach the asset store, after any required authorization.
    const response = await env.ASSETS.fetch(new Request(url, {method: request.method}));
    if (path === '/staff/handbook.html') {
      const headers = new Headers(response.headers);
      headers.set('Cache-Control', 'private, no-store');
      return new Response(response.body, {status: response.status, headers});
    }
    return response;
  }
};
JS
npx wrangler dev --ip 0.0.0.0 --port 8080 > dev.log 2>&1 &
cat dev.log

Cuando el servidor esté listo, compare las respuestas públicas, protegidas y desconocidas:

curl -i http://127.0.0.1:8080/api/health
curl -i http://127.0.0.1:8080/staff/handbook.html
curl -i http://127.0.0.1:8080/staff/handbook.html -H "Authorization: Bearer wrong-token"
curl -i http://127.0.0.1:8080/staff/handbook.html -H "Authorization: Bearer $STAFF_TOKEN"
curl -i --path-as-is http://127.0.0.1:8080/%73taff/handbook.html
curl -i http://127.0.0.1:8080/missing-page -H "Sec-Fetch-Mode: navigate"

Health ahora devuelve 200 {"status":"ok","service":"help-center"} aunque el archivo en conflicto siga existiendo. Las credenciales ausentes o incorrectas devuelven JSON 401; el token coincidente devuelve el HTML del manual sintético. La ruta codificada del personal también devuelve 401 y la navegación desconocida devuelve JSON 404. Eliminar el recurso ocultaría el problema de enrutamiento; consérvelo.

Actualice la vista previa web opcional del puerto 8080: el estado ahora debería indicar API status: ok. El endpoint del manual devuelve JSON 401 sin credenciales. Algunos navegadores integrados bloquean la navegación hacia esa respuesta y dejan visible la página anterior; use el resultado de curl anterior para inspeccionarla. Este comportamiento del navegador no demuestra que el acceso haya sido correcto. Use curl con el encabezado sintético para acceder de forma autorizada; no pegue el secreto en la barra de direcciones. Realice la verificación con el servidor en ejecución. También comprueba HEAD, las rutas codificadas, las grafías alternativas y los tipos de recursos públicos.

Compare la línea de estado con la vista previa anterior. API status: ok indica ahora que la página puede leer la respuesta de estado. Esta comprobación visual cubre la ruta pública de estado; use las respuestas de curl anteriores para evaluar el manual protegido.

Centro de ayuda después de corregir el enrutamiento, con API status ok

Desplegar los recursos y el controlador protegido

En este paso, desplegará únicamente la configuración corregida en su cuenta de aprendizaje. Detenga el proceso local actual usando su número real de jobs.

jobs
kill %1
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_tail:read

Complete el enlace y el código del dispositivo que se muestran en su navegador con la sesión iniciada, revise los permisos existentes de Wrangler y el acceso en segundo plano, y seleccione su cuenta de aprendizaje. Espere a que el terminal termine.

npx wrangler whoami --json

Confirme el nombre y el ID reales de la cuenta y, después, sustituya YOUR_ACCOUNT_ID por ese ID. Conserve el nombre original del recurso y la configuración corregida de los recursos.

cat > wrangler.jsonc <<CONFIG
{
  "name": "$WORKER_NAME",
  "main": "src/index.js",
  "compatibility_date": "2026-09-14",
  "account_id": "YOUR_ACCOUNT_ID",
  "workers_dev": true,
  "preview_urls": false,
  "assets": {
    "directory": "./public",
    "binding": "ASSETS",
    "html_handling": "none",
    "not_found_handling": "none",
    "run_worker_first": true
  }
}
CONFIG
npx wrangler deploy

Wrangler cargará el directorio público y desplegará su controlador. Copie la URL exacta de workers.dev que se muestre a continuación. Reutilice el subdominio existente de la cuenta de aprendizaje; quienes usen Wrangler por primera vez pueden seguir la indicación del subdominio disponible.

APP_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$APP_URL/staff/handbook.html"

Antes de cargar el secreto, esta ruta devuelve 503 staff_unconfigured: el controlador se ejecuta primero y rechaza la solicitud de forma segura. .dev.vars es una configuración local y el despliegue no la cargó. Si la propagación inicial del nombre de host retrasa la respuesta, vuelva a intentarlo brevemente antes de investigar un error persistente.

npx wrangler secret bulk .dev.vars
npx wrangler secret list

Confirme que STAFF_TOKEN aparece como secret_text, sin mostrar su valor. El despliegue del secreto puede tardar un poco en llegar a todas las ubicaciones que sirven el contenido. Si las siguientes solicitudes todavía devuelven staff_unconfigured, espere 10 segundos y repítalas durante un máximo de dos minutos. Exija un 401 estable sin la credencial y un 200 con ella antes de usar la verificación. Un desajuste persistente requiere investigación; no acepte 503 como resultado final ni cambie la política de autorización para conseguir que una comprobación pase.

curl -i "$APP_URL/"
curl -i "$APP_URL/api/health"
curl -i "$APP_URL/staff/handbook.html"
curl -i "$APP_URL/staff/handbook.html" -H "Authorization: Bearer $STAFF_TOKEN"
curl -i "$APP_URL/missing-page" -H "Sec-Fetch-Mode: navigate"

Espere HTML público, JSON de estado, 401 sin la credencial, HTML del manual con la credencial y 404 para la página desconocida. En la misma cuenta del Dashboard, abra Compute → Workers & Pages, localice el Worker exacto y confirme su URL pública. Use la verificación para comprobar la propiedad real, los bindings desplegados, el contenido de los recursos y el comportamiento de autorización. También puede abrir la página de inicio pública en su propio navegador; no envíe el token del personal mediante una URL. La puerta de acceso mediante un token sintético es una lección sobre enrutamiento, no un sistema completo de identidad del personal.

En la pestaña Overview del Worker, compare el nombre de la ruta de navegación y la dirección workers.dev vinculada con el resultado del despliegue. El nombre y el subdominio de esta captura son ejemplos; el nombre generado y el subdominio de su cuenta serán diferentes. Esta es la dirección pública desplegada, mientras que Web 8080 muestra su servidor de desarrollo local. Abrir este Worker existente no requiere crear otra aplicación.

Worker del centro de ayuda desplegado y su dirección pública en Overview

Eliminar el despliegue del centro de ayuda

En este paso, eliminará el Worker del laboratorio y sus recursos y bindings de secretos asociados mientras sigue autorizado. Confirme el nombre único y la cuenta:

cat wrangler.jsonc
npx wrangler delete

Compruebe el nombre exacto del laboratorio en la solicitud y pulse la única tecla y. Wrangler 4.131.1 puede mostrar el error documentado de autenticación heredada de Workers Sites KV después de la eliminación. No amplíe los permisos ni use ese mensaje como prueba de eliminación. Actualice Workers & Pages y use la verificación: un inventario autorizado correcto debe mostrar que este nombre está ausente. Conserve los recursos no relacionados, la cuenta y su subdominio de workers.dev.

Eliminar la credencial local y desconectarse

En este paso, eliminará la credencial sintética local después de verificar la limpieza en la nube y, a continuación, desconectará esta VM.

rm .dev.vars
unset STAFF_TOKEN
npx wrangler logout
npx wrangler whoami --json

Espere ver explícitamente "loggedIn": false; cuando este resultado estructurado esté presente, es normal que el comando termine con un estado de salida distinto de cero por falta de autenticación. Use la verificación y finalice la VM. La sesión del navegador puede seguir disponible; ni finalizar la VM ni cerrar la sesión elimina los recursos de la nube por usted.

Resumen

Observó el enrutamiento con prioridad para los recursos y, después, utilizó el manejo con prioridad para el Worker a fin de mantener las respuestas de la API y la autorización por delante de los archivos coincidentes. La estructura proporcionada del centro de ayuda conservó el HTML, el CSS y el JavaScript del navegador públicos, mientras que el manejo explícito de las rutas bloqueó las solicitudes no autenticadas del personal y las rutas desconocidas. Probó rutas codificadas y navegación con formato de navegador, desplegó el sitio corregido con una carga separada del secreto y, finalmente, verificó la eliminación y el cierre de sesión.

Elija deliberadamente el orden de enrutamiento siempre que los archivos estáticos y la política de la aplicación compartan un nombre de host. Una respuesta local por sí sola no demuestra que la configuración desplegada ni la identidad de la cuenta sean correctas.