Если вы видите это, значит, я еще не придумал, что написать.
В нашей продуктовой компании используется много Laravel проектов, и сервисов которые решают свои задачи. Под каждый проект мы создавали поддомены, например:
При этом каждые проекты разделены под разные региональные зоны: .ru
, .by
, .kz
, .tr
. А также у нас существует несколько брендов, в каждой региональной зоне, это привело к тому, что проекты начали занимать множество доменов и поддоменов и каждый раз, когда нам нужно развернуть новый проект или провести эксперимент – приходилось мучаться с созданием новых доменов, выпуском сертификатов и настройкой логирования.
Мы устали плодить поддомены под каждый проект и решили перевести всё на единый домен domain.tech
, где региональность и бренд задаётся поддоменом третьего уровня, а проекты разделены внутри этого домена.
Было: {project}.{brand}.domain.{region}
Стало: {brand}-{region}.domain.tech/{project}
Как могло быть еще (для нас это показалось не так удобно): {brand}.domain.tech/{region}/{project}
Плюсы
domain.tech
.Минусы
Чтобы перейти на единую доменную систему – необходимо настроить nginx правильным образом: роутинг по приложениям осуществляется при помощи location, в рамках одного server_name
Пример конфигурации nginx:
server {
listen 80;
listen 443 ssl;
server_name *.domain.tech;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /api/ {
proxy_pass http://api-container;
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;
proxy_set_header X-Forwarded-Prefix /api;
}
location /call-center/ {
proxy_pass http://call-center-container;
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;
proxy_set_header X-Forwarded-Prefix /call-center;
rewrite ^/call-center/(.*)$ /$1 break;
}
location /office/ {
proxy_pass http://office-container;
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;
proxy_set_header X-Forwarded-Prefix /api;
}
}
Здесь мы проксируем запрос на основе названия проекта и передаём запрос в контейнер (или fpm) нужного нам, при этом убираем из запроса название проекта, чтобы Laravel в проекте штатно обрабатывал роуты, якобы запрос пришёл на корень проекта. Это позволит мигрировать текущие проекты и разворачивать локально как обычно, без каких либо сложностей и имитации распределенной URL структуры.
Важно прокинуть заголовок X-Forwarded-Path
, по этому заголовку Laravel сможет понять, как правильно генерировать ссылки для Frontend части и редиректов. Чтобы ваш Laravel проект начал доверять этому заголовку – нужно настроить TrustProxies.
Проверьте, что у вас включен middleware \Illuminate\Http\Middleware\TrustProxies::class
Добавьте проверьте настройки config/trustedproxy.php
<?php
use Symfony\Component\HttpFoundation\Request;
return [
'proxies' => [
'10.0.0.0/8',
'172.16.0.0/12',
'192.168.0.0/16',
'127.0.0.0/8',
],
'headers' => Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_PREFIX |
Request::HEADER_X_FORWARDED_AWS_ELB
];
В headers
должен быть разрешён заголовок HEADER_X_FORWARDED_PREFIX
.
В proxies
можно указать маски всех локальных IP адресов, так не придётся мучаться с настройками динамических сетей.
Если ваш nginx находится не в локальной сети, то добавьте IP адрес сервера nginx в ваш список proxies
Нужно правильно задать APP_URL
, вписав туда фактическую ссылку на проект, это позволит генерировать правильные ссылки через CLI окружение (консоль, команды, очереди).
Теперь нужно проверить, чтобы во всех шаблонах для путей статических файлов использовался хелпер asset()
, если поизучать исходники этой функции, то вы увидите, что генерация ссылок происходит с использованием заголовка X-Forwarded-Path
, X-Forwarded-Proto
, X-Forwarded-Host
именно для этого мы и прокидывали все эти заголовки.
Ссылки должны строиться через хелпер url()
, это позволит точно также генерировать правильные ссылки внутри проекта.
После того, как мы убедились, что для статических файлов мы используем asset()
, а для внутренних путей url()
, нужно заменить все глобальные пути в JS файлах на относительные. Например если мы отправляли запросы таким образом: axios.get('/api/entity')
, это нужно переделать вот так: axios.get('api/entity')
Тоже самое нужно проделать во всех href="/example"
и src="/example"
атрибутах, например для <a>
, <script>
и <link>
если по каким-то причинам мы не смогли использовать asset()
и url()
хелперы. Такое может случиться, если ссылки на статические файлы нам приходят из другого проекта, а сами статические файлы хранятся в текущем проекте.
После того, как мы перевели ссылки с глобальных на статические – необходимо в начало тега <head>
прописать атрибут <base href="url('/')/">
этот лайфках позволит работать с относительными ссылками из корня проекта.
У вас могут начаться проблемы с css файлами, в которых ссылки на статические изображения забиты внутри public css файла, например background-image: url(assets/img/example.png)
. Чтобы решить эту проблему – перенесите генерацию стилей в vite или vue файлы, тогда css и assets файлы будут находится в одной build директории и у вас получится решить эту проблему. Если изображений несколько и они небольшие (например несколько иконок), то вы можете не использовать подгрузку отдельного файла с изображением, а представить в виде base64 ссылки или svg прямо внутри css url()
хелпера.
Для того, чтобы собранные файлы из public/build стали относительными – нужно задать относительный путь в base параметре vite:
export default {
base: './',
// ...
}
Если вы начинаете новый проект, то я советую вам изначально для ваших frontend путей использовать BASE_URL
, пусть оно будет всегда по умолчанию '/'
, но в таком случае вы не столкнётесь с множеством проблем, если ваш проект будет хоститься не из корня домена. BASE_URL
мы можете передавать из проекта, самым примитивным образом из <head>
тега:
<script>window.BASE_URL = "{{ url('/') }}"; </script>
Также полезным будет иметь такую хелпер функцию, похожую на Laravel:
function url(path) {
const origin = window.location.origin;
// Очищаем лишний первый и последний слеши
const baseUrl = (window.BASE_URL ?? '').replace(/^\/|\/$/g, '');
// Очищаем лишний первый слеш
path = (path ?? '').replace(/^\//g, '');
// Выводим 3 компонента, разделяя слешами
return [origin, baseUrl, path].join('/');
}
Этой функцией вы сможете также как и на Laravel генерировать ссылки на Frontend части вашего приложения, относительно базового URL.
{message}