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.
Índice
O problema
O Mindtraining Platform usa routing 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 utilizador:
Navega diretamente para
https://yoursite.com/games/crossword/archiveGuarda nos favoritos um deep link
Atualiza a página numa rota diferente da raiz
…o navegador envia um pedido ao servidor para esse caminho exato. Sem configuração, o servidor procura um ficheiro em /games/crossword/archive e devolve 404, porque esse caminho existe apenas no router da SPA, não em disco.
A solução
Todos os pedidos para rotas SPA têm de servir o mesmo ficheiro de entrada (index.html ou script.js). A SPA depois lê a URL e renderiza a vista correta. Isto é conseguido através de:
URL rewrite — mapear internamente todos os caminhos SPA para o ficheiro de entrada
Proxy reverso — encaminhar os pedidos para um backend que serve a SPA
Fallback / catch-all — tratar qualquer caminho não estático como rota SPA
Apache
Usando .htaccess (mod_rewrite)
Coloque isto na raiz do documento ou no subdiretório da SPA (por exemplo, /games/.htaccess):
apache
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /games/
# Não reescrever ficheiros ou diretórios existentes
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Reescrever tudo o resto 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á ativado:a2enmod rewrite(Debian/Ubuntu).AllowOverride Alltem de estar definido para que.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 num subdiretório (por exemplo /games)
Se a SPA vive sob /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 num 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 for 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 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 não puder 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 ficheiros estáticos (JS, CSS, imagens)
app.use(express.static(path.join(__dirname, 'dist')))
// Fallback 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 (alojamento estático)
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 ficheiro
if (uri.includes('.') && !uri.endsWith('.html')) {
return request
}
// Fallback 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 do lado do servidor. Opções:
Use o truque do 404.html: crie uma página 404 personalizada que carregue a SPA e redirecione do lado do cliente. Não é ideal para SEO nem para deep links.
Use um hash router do lado do cliente (
#/games/crossword) em vez de routing baseado em path. Não requer configuração de servidor, mas os URLs ficam menos amigáveis.Aloje a SPA noutro local (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 cobrem o basepath completo. Por exemplo, se basepath for /games:
Apache:
RewriteBase /games/e servirindex.htmlpara todos os pedidos que não sejam ficheiros dentro de/gamesNginx:
location /games { try_files $uri $uri/ /games/index.html; }Express: montar estáticos + fallback em
/games