--- title: "Publicar documentación OpenAPI con Swagger UI y Nginx" description: "Publica la documentación de tu API en Ubuntu 24.04 con Swagger UI y ReDoc servidos por Nginx, valida la especificación OpenAPI con Spectral y activa HTTPS." category: "API y Microservicios" language: es source: https://cubepath.com/es/docs/api-y-microservicios/servidor-de-documentacion-swagger-openapi updated: 2026-09-25 --- # Publicar documentación OpenAPI con Swagger UI y Nginx OpenAPI es el formato estándar para describir APIs REST en un archivo YAML o JSON, y a partir de ese archivo herramientas como Swagger UI y ReDoc generan documentación interactiva. En esta guía publicarás la documentación de una API en un servidor Ubuntu 24.04 con Nginx: validarás la especificación con Spectral, servirás Swagger UI y ReDoc como páginas estáticas, organizarás varias versiones de la API, activarás HTTPS con Let's Encrypt y, si la documentación es interna, la protegerás con contraseña. ## Requisitos previos - Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Un dominio con un registro A que apunte a la IP del servidor. En esta guía se usa **`docs.your_domain`**; sustitúyelo por el tuyo. - Los puertos 80 y 443 accesibles desde Internet. No hace falta Docker ni Node.js: Swagger UI y ReDoc son aplicaciones JavaScript que se ejecutan en el navegador, así que el servidor solo sirve archivos estáticos. ## Paso 1: Instalar Nginx y preparar el directorio Instala Nginx y permite el tráfico HTTP y HTTPS en el cortafuegos: ```bash sudo apt update sudo apt install nginx sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable ``` Crea la estructura de directorios. Cada versión mayor de la API tendrá su propio subdirectorio en `specs/`, y Swagger UI irá en `/swagger/`: ```bash sudo mkdir -p /var/www/api-docs/{specs/v1,swagger} sudo chown -R "$USER":www-data /var/www/api-docs ``` Comprueba que Nginx está activo: ```bash systemctl status nginx --no-pager ``` ```output ● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled) Active: active (running) since ... ``` ## Paso 2: Escribir la especificación OpenAPI Trabaja con la especificación en tu directorio personal (en un proyecto real vivirá en el mismo repositorio que el código de la API) y publícala en `/var/www` solo cuando pase la validación. ```bash mkdir -p ~/api-spec nano ~/api-spec/openapi.yaml ``` Este ejemplo describe una API de usuarios con autenticación JWT, esquemas reutilizables y respuestas de error comunes: ```yaml openapi: 3.1.0 info: title: API de ejemplo version: 1.2.0 description: | API de gestión de usuarios. Todas las rutas requieren un token JWT en la cabecera `Authorization: Bearer `. contact: name: Equipo de API email: api@your_domain license: name: Apache 2.0 identifier: Apache-2.0 servers: - url: https://api.your_domain/v1 description: Producción tags: - name: Usuarios description: Alta y consulta de usuarios security: - BearerAuth: [] paths: /usuarios: get: tags: [Usuarios] summary: Listar usuarios description: Devuelve la lista paginada de usuarios. operationId: listarUsuarios parameters: - name: page in: query schema: type: integer minimum: 1 default: 1 - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 20 responses: '200': description: Lista de usuarios content: application/json: schema: type: object properties: items: type: array items: $ref: '#/components/schemas/Usuario' total: type: integer '401': $ref: '#/components/responses/NoAutorizado' post: tags: [Usuarios] summary: Crear usuario description: Da de alta un usuario nuevo. operationId: crearUsuario requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/NuevoUsuario' responses: '201': description: Usuario creado content: application/json: schema: $ref: '#/components/schemas/Usuario' '401': $ref: '#/components/responses/NoAutorizado' /usuarios/{id}: get: tags: [Usuarios] summary: Obtener un usuario description: Devuelve un usuario por su identificador. operationId: obtenerUsuario parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Usuario encontrado content: application/json: schema: $ref: '#/components/schemas/Usuario' '401': $ref: '#/components/responses/NoAutorizado' '404': $ref: '#/components/responses/NoEncontrado' components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT schemas: Usuario: type: object required: [id, email, nombre] properties: id: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 email: type: string format: email example: usuario@example.com nombre: type: string example: Juan García createdAt: type: string format: date-time readOnly: true NuevoUsuario: type: object required: [email, nombre] properties: email: type: string format: email nombre: type: string minLength: 2 maxLength: 100 Error: type: object required: [detail] properties: detail: type: string example: El recurso solicitado no existe responses: NoAutorizado: description: Token ausente, inválido o caducado content: application/json: schema: $ref: '#/components/schemas/Error' NoEncontrado: description: Recurso no encontrado content: application/json: schema: $ref: '#/components/schemas/Error' ``` Si tu API está hecha con un framework que genera la especificación (FastAPI, NestJS, Spring con springdoc), exporta ese archivo en lugar de escribirlo a mano y sigue el resto de la guía igual. ## Paso 3: Validar la especificación con Spectral Un error en la especificación hace que Swagger UI muestre una página en blanco o incompleta, así que conviene validarla antes de publicarla. Spectral es un linter de OpenAPI que se distribuye como binario autónomo, sin dependencias de Node.js. Descarga la última versión para tu arquitectura (usa `spectral-linux-arm64` en servidores ARM): ```bash curl -fsSLo spectral https://github.com/stoplightio/spectral/releases/latest/download/spectral-linux-x64 sudo install -m 755 spectral /usr/local/bin/spectral rm spectral spectral --version ``` Spectral necesita un conjunto de reglas. Crea uno global que extienda las reglas oficiales de OpenAPI: ```bash sudo mkdir -p /etc/spectral echo 'extends: ["spectral:oas"]' | sudo tee /etc/spectral/ruleset.yaml ``` Valida la especificación: ```bash spectral lint --ruleset /etc/spectral/ruleset.yaml ~/api-spec/openapi.yaml ``` Si todo está bien, Spectral indica que no hay resultados y termina con código 0. Si hay problemas, muestra cada uno con su línea y severidad: ```output /home/your_user/api-spec/openapi.yaml 45:13 error invalid-ref '#/components/schemas/Usuarios' does not exist paths./usuarios.get.responses[200]... ``` Los resultados de tipo `warning` (una operación sin descripción, por ejemplo) no impiden que la documentación funcione; los de tipo `error` sí deben corregirse. Con `--fail-severity error` el comando solo falla por errores, que es lo que usarás en el script de publicación. ## Paso 4: Crear la página de Swagger UI Swagger UI muestra cada operación con un formulario "Try it out" para lanzar peticiones reales desde el navegador. Se carga desde el paquete `swagger-ui-dist` de jsDelivr, fijado a la versión mayor 5 para no recibir cambios incompatibles: ```bash nano /var/www/api-docs/swagger/index.html ``` ```html API de ejemplo - Swagger UI
``` La opción `urls` con `StandaloneLayout` añade un selector en la barra superior para cambiar entre versiones de la API. Cuando publiques la v2, solo tendrás que añadir otra entrada a la lista. > Nota: si la documentación debe funcionar sin acceso a Internet (una red interna aislada), descarga los archivos `swagger-ui.css`, `swagger-ui-bundle.js` y `swagger-ui-standalone-preset.js` desde jsDelivr a `/var/www/api-docs/swagger/` y cambia las rutas a relativas. ## Paso 5: Crear la página de ReDoc ReDoc genera una documentación de lectura en tres columnas, más cómoda para referencia pública, aunque sin formulario para probar peticiones. Será la página principal del sitio: ```bash nano /var/www/api-docs/index.html ``` ```html API de ejemplo - Referencia ``` ## Paso 6: Configurar Nginx Crea el bloque de servidor para el dominio de la documentación: ```bash sudo nano /etc/nginx/sites-available/api-docs ``` ```nginx server { listen 80; listen [::]:80; server_name docs.your_domain; root /var/www/api-docs; index index.html; location / { try_files $uri $uri/ =404; } location /specs/ { types { application/yaml yaml yml; application/json json; } add_header Cache-Control "no-cache"; add_header Access-Control-Allow-Origin "*"; try_files $uri =404; } } ``` La configuración de `/specs/` hace tres cosas: - **Tipo MIME correcto.** El `mime.types` de Nginx no incluye YAML, así que sin el bloque `types` los archivos se servirían como `application/octet-stream` y algunos navegadores los descargarían en lugar de pasarlos a Swagger UI. - **`Cache-Control: no-cache`.** El navegador revalida la especificación en cada visita, de modo que una versión recién publicada aparece al momento, sin perder las respuestas `304 Not Modified` cuando no ha cambiado. - **CORS abierto.** Permite que otras herramientas (Postman, un Swagger UI alojado en otro dominio, generadores de clientes) descarguen la especificación. Si es privada, elimina esa línea. Activa el sitio, comprueba la sintaxis y recarga Nginx: ```bash sudo ln -s /etc/nginx/sites-available/api-docs /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx ``` ```output nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ``` ## Paso 7: Publicar versiones con un script Copiar la especificación a mano es fácil de olvidar o de hacer sin validar. Este script valida el archivo con Spectral y solo si no hay errores lo instala en el directorio de la versión, primero en un archivo temporal y después con `mv` para que nadie descargue un archivo a medio copiar: ```bash sudo nano /usr/local/bin/publicar-spec ``` ```bash #!/usr/bin/env bash set -euo pipefail if [[ $# -ne 2 ]]; then echo "Uso: $(basename "$0") (ejemplo: openapi.yaml v2)" >&2 exit 1 fi spec="$1" version="$2" destino="/var/www/api-docs/specs/${version}" if [[ ! "$version" =~ ^v[0-9]+$ ]]; then echo "La versión debe tener el formato v1, v2, v3..." >&2 exit 1 fi spectral lint --ruleset /etc/spectral/ruleset.yaml --fail-severity error "$spec" install -d -m 755 -o "${SUDO_USER:-root}" -g www-data "$destino" install -m 644 "$spec" "${destino}/openapi.yaml.tmp" mv "${destino}/openapi.yaml.tmp" "${destino}/openapi.yaml" echo "Publicada en https://docs.your_domain/specs/${version}/openapi.yaml" ``` Hazlo ejecutable y publica la especificación como v1: ```bash sudo chmod 755 /usr/local/bin/publicar-spec sudo publicar-spec ~/api-spec/openapi.yaml v1 ``` Comprueba que Nginx la sirve con el tipo correcto: ```bash curl -sI http://docs.your_domain/specs/v1/openapi.yaml ``` ```output HTTP/1.1 200 OK Server: nginx/1.24.0 (Ubuntu) Content-Type: application/yaml Cache-Control: no-cache Access-Control-Allow-Origin: * ... ``` Abre `http://docs.your_domain/` para ver ReDoc y `http://docs.your_domain/swagger/` para Swagger UI. Cuando saques una versión mayor nueva, publícala con `sudo publicar-spec openapi.yaml v2` y añade `{ url: "/specs/v2/openapi.yaml", name: "v2" }` a la lista `urls` de Swagger UI; la v1 sigue disponible para los clientes que aún la usan. Si la especificación vive en un repositorio Git, lo natural es ejecutar este mismo `spectral lint` en el pipeline de CI en cada pull request, y publicar solo desde la rama principal. ## Paso 8: Activar HTTPS con Let's Encrypt Swagger UI envía tokens reales cuando se usa "Try it out", así que la documentación debe servirse por HTTPS. Instala Certbot con su plugin de Nginx: ```bash sudo apt install certbot python3-certbot-nginx ``` Solicita el certificado. Certbot modifica el bloque de servidor para servir HTTPS y redirigir HTTP: ```bash sudo certbot --nginx -d docs.your_domain ``` El paquete instala un temporizador de systemd que renueva el certificado automáticamente. Comprueba que la renovación funcionará: ```bash sudo certbot renew --dry-run ``` Si tus servidores de API (`servers` en la especificación) están en otro dominio, esa API debe responder a las peticiones preflight de CORS con `Access-Control-Allow-Origin: https://docs.your_domain` para que "Try it out" funcione desde el navegador. ## Paso 9 (opcional): Proteger la documentación interna Si la documentación no debe ser pública, añade autenticación básica. Instala `htpasswd` y crea el primer usuario (te pedirá la contraseña): ```bash sudo apt install apache2-utils sudo htpasswd -c /etc/nginx/.htpasswd-docs your_user ``` Para añadir más usuarios, repite el comando sin `-c`, que sobrescribiría el archivo. Después abre la configuración del sitio: ```bash sudo nano /etc/nginx/sites-available/api-docs ``` Añade estas dos líneas dentro del bloque `server` que escucha en el puerto 443 (el que ha creado Certbot), justo debajo de `server_name`: ```nginx auth_basic "Documentación interna"; auth_basic_user_file /etc/nginx/.htpasswd-docs; ``` Aplica los cambios y comprueba que sin credenciales se devuelve un 401: ```bash sudo nginx -t && sudo systemctl reload nginx curl -sI https://docs.your_domain/ | head -1 ``` ```output HTTP/1.1 401 Unauthorized ``` Como alternativa, si solo tu red de oficina o tu VPN deben acceder, sustituye la autenticación por reglas `allow 203.0.113.0/24;` y `deny all;` con tus rangos reales. ## Solución de problemas **Swagger UI muestra "Failed to load API definition".** Abre la URL de la especificación directamente en el navegador: si devuelve 404, revisa la ruta en `urls`; si la descarga en lugar de mostrarla, falta el bloque `types` en `location /specs/`. La consola del navegador indica si el problema es de CORS. **Swagger UI o ReDoc muestran una página en blanco.** La especificación tiene un error de sintaxis YAML o una referencia rota. Ejecuta `spectral lint` sobre el archivo publicado para localizarlo. **Los cambios no aparecen.** Comprueba que has publicado en el directorio de la versión correcta con `ls -l /var/www/api-docs/specs/*/` y fuerza la recarga del navegador. Si usas una CDN delante del dominio, purga su caché para `/specs/`. **`certbot` falla con un error de validación.** El registro A de `docs.your_domain` no apunta todavía al servidor o el puerto 80 está cerrado. Comprueba con `dig +short docs.your_domain` y `sudo ufw status`. ## Conclusión Tienes un sitio de documentación de API servido por Nginx con HTTPS, con ReDoc como referencia de lectura, Swagger UI para probar peticiones, varias versiones de la especificación y un script que impide publicar un archivo inválido. Como siguientes pasos, puedes ejecutar la validación de Spectral en el pipeline de CI de tu API, añadir reglas propias al ruleset (por ejemplo, exigir `operationId` en camelCase) y generar clientes SDK a partir de la misma especificación con OpenAPI Generator.