Appearance
URLs amigáveis, reescrita de URL e proxy reverso
Este documento explica como integrar o Mindtraining Platform com URLs amigáveis e como configurar reescrita de URL ou proxy reverso para que navegação direta, favoritos e refresh funcionem corretamente.
Sumário
O problema
O Mindtraining Platform usa roteamento client-side: o navegador carrega um único index.html (ou equivalente) e o JavaScript atualiza a visualização com base no caminho da URL. Quando um usuário:
Navega diretamente para
https://yoursite.com/games/crossword/archiveSalva um favorito com deep link
Atualiza a página em uma rota que não seja a raiz
…o navegador envia uma requisição ao servidor para aquele caminho exato. Sem configuração, o servidor procura um arquivo em /games/crossword/archive e retorna 404, porque esse caminho existe apenas no roteador da SPA, não em disco.
A solução
Todas as requisições para rotas da SPA precisam servir o mesmo arquivo de entrada (index.html ou script.js). A SPA então lê a URL e renderiza a view correta. Isso é feito por meio de:
URL rewrite — mapear internamente todos os caminhos da SPA para o arquivo de entrada
Proxy reverso — encaminhar as requisições para um backend que sirva a SPA
Fallback / catch-all — tratar qualquer caminho que não seja asset como rota da SPA
Apache
Usando .htaccess (mod_rewrite)
Coloque isso no document root ou no subdiretório da SPA (por exemplo, /games/.htaccess):
apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /games/
# Não reescrever arquivos ou diretórios existentes
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Reescrever todo o restante para index.html
RewriteRule ^ index.html [L]
</IfModule>Notas:
Substitua
/games/pelo base path da sua SPA. Use/se a SPA estiver na raiz.Garanta que
mod_rewriteestá habilitado:a2enmod rewrite(Debian/Ubuntu).AllowOverride Allprecisa estar configurado para que o.htaccessseja respeitado.
Usando VirtualHost (sem .htaccess)
apache
<VirtualHost *:80>
ServerName yoursite.com
DocumentRoot /var/www/mindtraining
<Directory /var/www/mindtraining>
Options -Indexes +FollowSymLinks
AllowOverride None
Require all granted
RewriteEngine On
RewriteBase /games/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]
</Directory>
</VirtualHost>SPA em um subdiretório (por exemplo /games)
Se a SPA vive em /games e o entrypoint é index.html nessa pasta:
apache
<Directory /var/www/html/games>
RewriteEngine On
RewriteBase /games/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ /games/index.html [L]
</Directory>Nginx
Fallback básico para SPA
nginx
server {
listen 80;
server_name yoursite.com;
root /var/www/mindtraining;
location / {
try_files $uri $uri/ /index.html;
}
}SPA em um subdiretório (por exemplo /games)
nginx
server {
listen 80;
server_name yoursite.com;
root /var/www/html;
location /games {
alias /var/www/html/games;
try_files $uri $uri/ /games/index.html;
}
}Com proxy reverso para uma origem estática/CDN
Se a SPA é servida a partir de um CDN ou outra origem:
nginx
server {
listen 80;
server_name yoursite.com;
location / {
proxy_pass <https://cdn.example.com/mindtraining/>;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Fallback da SPA: se o upstream responder 404, servir index
proxy_intercept_errors on;
error_page 404 = /index.html;
}
}Subpath + proxy mais robusto
nginx
server {
listen 80;
server_name yoursite.com;
location /games/ {
proxy_pass <https://cdn.example.com/mindtraining/>;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Não reescrever assets estáticos
proxy_intercept_errors on;
proxy_next_upstream error timeout http_404;
error_page 404 = @spa_fallback;
}
location @spa_fallback {
rewrite ^ /games/index.html break;
proxy_pass <https://cdn.example.com/mindtraining/>;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}Alternativas (sem Apache/Nginx)
Se você não pode usar Apache ou Nginx (por exemplo, serverless, PaaS ou um servidor Node simples), use uma destas abordagens.
1. Node.js (Express)
js
const express = require('express')
const path = require('path')
const app = express()
const PORT = process.env.PORT || 3000
// Servir arquivos estáticos (JS, CSS, imagens)
app.use(express.static(path.join(__dirname, 'dist')))
// Fallback da SPA: todas as outras rotas servem index.html
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})SPA em subdiretório (por exemplo /games):
js
const express = require('express')
const path = require('path')
const app = express()
const BASE = '/games'
app.use(BASE, express.static(path.join(__dirname, 'dist')))
app.get(`${BASE}/*`, (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})
app.listen(process.env.PORT || 3000)2. Vercel (vercel.json)
json
{
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}Para um subpath:
json
{
"rewrites": [
{ "source": "/games/:path*", "destination": "/games/index.html" }
]
}3. Netlify (_redirects ou netlify.toml)
_redirects (em public/ ou na raiz do projeto):
txt
/* /index.html 200netlify.toml:
toml
[[redirects]]
from = "/*"
to = "/index.html"
status = 200Para o subpath /games:
txt
/games/* /games/index.html 2004. AWS S3 + CloudFront (hospedagem estática)
O S3 não suporta rewrites. Use CloudFront Functions ou Lambda@Edge:
CloudFront Function (viewer request ou origin request):
js
function handler(event) {
var request = event.request
var uri = request.uri
// Não reescrever se parecer um arquivo
if (uri.includes('.') && !uri.endsWith('.html')) {
return request
}
// Fallback da SPA
if (!uri.endsWith('/') && !uri.includes('.')) {
request.uri = '/index.html'
} else if (uri.endsWith('/')) {
request.uri = uri + 'index.html'
}
return request
}5. Firebase Hosting (firebase.json)
json
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}6. GitHub Pages
O GitHub Pages não suporta rewrites no lado do servidor. Opções:
Use o truque do 404.html: crie uma página 404 personalizada que carregue a SPA e faça o redirecionamento no cliente. Não é ideal para SEO nem para deep links.
Use um hash router no cliente (
#/games/crossword) em vez de roteamento baseado em path. Não requer configuração de servidor, mas as URLs ficam menos amigáveis.Hospede a SPA em outro lugar (Vercel, Netlify etc.) e aponte o domínio para lá.
Especificidades do Mindtraining Platform
Esta plataforma usa TanStack Router com um basepath dinâmico vindo da API. Rotas típicas:
| Padrão de path | Exemplo |
|---|---|
| Home | / ou /{basepath}/ |
| Jogo de hoje | /{basepath}/crossword/ |
| Arquivo | /{basepath}/crossword/archive |
| Estatísticas | /{basepath}/crossword/statistics |
| Data específica | /{basepath}/crossword/2024-03-18 |
O basepath é configurado por site/domínio. Garanta que as suas regras cubram o basepath completo. Por exemplo, se basepath for /games:
Apache:
RewriteBase /games/e servirindex.htmlpara todas as requisições que não sejam arquivos dentro de/gamesNginx:
location /games { try_files $uri $uri/ /games/index.html; }Express: montar estáticos + fallback em
/games