fastapi/docs/tr/docs/advanced/behind-a-proxy.md

467 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Proxy Arkasında Çalıştırma { #behind-a-proxy }
Birçok durumda, FastAPI uygulamanızın önünde Traefik veya Nginx gibi bir **proxy** kullanırsınız.
Bu proxy'ler HTTPS sertifikalarını ve diğer bazı işleri üstlenebilir.
## Proxy Forwarded Header'ları { #proxy-forwarded-headers }
Uygulamanızın önündeki bir **proxy**, request'leri **server**'ınıza göndermeden önce genelde bazı header'ları dinamik olarak ayarlar. Böylece server, request'in proxy tarafından **forward** edildiğini; domain dahil orijinal (public) URL'yi, HTTPS kullanıldığını vb. bilgileri anlayabilir.
**Server** programı (örneğin **FastAPI CLI** üzerinden **Uvicorn**) bu header'ları yorumlayabilir ve ardından bu bilgiyi uygulamanıza aktarabilir.
Ancak güvenlik nedeniyle, server güvenilir bir proxy arkasında olduğunu bilmediği için bu header'ları yorumlamaz.
/// note | Teknik Detaylar
Proxy header'ları şunlardır:
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-For" class="external-link" target="_blank">X-Forwarded-For</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Proto" class="external-link" target="_blank">X-Forwarded-Proto</a>
* <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host" class="external-link" target="_blank">X-Forwarded-Host</a>
///
### Proxy Forwarded Header'larını Etkinleştirme { #enable-proxy-forwarded-headers }
FastAPI CLI'yi `--forwarded-allow-ips` *CLI Option*'ı ile başlatıp, bu forwarded header'ları okumada güvenilecek IP adreslerini verebilirsiniz.
Bunu `--forwarded-allow-ips="*"` olarak ayarlarsanız, gelen tüm IP'lere güvenir.
**Server**'ınız güvenilir bir **proxy** arkasındaysa ve onunla sadece proxy konuşuyorsa, bu ayar server'ın o **proxy**'nin IP'si her neyse onu kabul etmesini sağlar.
<div class="termy">
```console
$ fastapi run --forwarded-allow-ips="*"
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### HTTPS ile Redirect'ler { #redirects-with-https }
Örneğin `/items/` adında bir *path operation* tanımladığınızı düşünelim:
{* ../../docs_src/behind_a_proxy/tutorial001_01_py310.py hl[6] *}
Client `/items`'a gitmeye çalışırsa, varsayılan olarak `/items/`'a redirect edilir.
Ancak *CLI Option* `--forwarded-allow-ips` ayarlanmadan önce, `http://localhost:8000/items/`'a redirect edebilir.
Oysa uygulamanız `https://mysuperapp.com` üzerinde host ediliyor olabilir ve redirect'in `https://mysuperapp.com/items/` olması gerekir.
Artık `--proxy-headers` ayarını yaparak FastAPI'nin doğru adrese redirect edebilmesini sağlarsınız. 😎
```
https://mysuperapp.com/items/
```
/// tip | İpucu
HTTPS hakkında daha fazla bilgi için [HTTPS Hakkında](../deployment/https.md){.internal-link target=_blank} rehberine bakın.
///
### Proxy Forwarded Header'ları Nasıl Çalışır { #how-proxy-forwarded-headers-work }
**Proxy**'nin, client ile **application server** arasında forwarded header'ları nasıl eklediğini gösteren görsel bir temsil:
```mermaid
sequenceDiagram
participant Client
participant Proxy as Proxy/Load Balancer
participant Server as FastAPI Server
Client->>Proxy: HTTPS Request<br/>Host: mysuperapp.com<br/>Path: /items
Note over Proxy: Proxy adds forwarded headers
Proxy->>Server: HTTP Request<br/>X-Forwarded-For: [client IP]<br/>X-Forwarded-Proto: https<br/>X-Forwarded-Host: mysuperapp.com<br/>Path: /items
Note over Server: Server interprets headers<br/>(if --forwarded-allow-ips is set)
Server->>Proxy: HTTP Response<br/>with correct HTTPS URLs
Proxy->>Client: HTTPS Response
```
**Proxy**, orijinal client request'ini araya girerek (intercept) alır ve request'i **application server**'a iletmeden önce özel *forwarded* header'ları (`X-Forwarded-*`) ekler.
Bu header'lar, aksi halde kaybolacak olan orijinal request bilgilerini korur:
* **X-Forwarded-For**: Orijinal client'ın IP adresi
* **X-Forwarded-Proto**: Orijinal protokol (`https`)
* **X-Forwarded-Host**: Orijinal host (`mysuperapp.com`)
**FastAPI CLI** `--forwarded-allow-ips` ile yapılandırıldığında bu header'lara güvenir ve örneğin redirect'lerde doğru URL'leri üretmek için bunları kullanır.
## Path Prefix'i Kırpılan (Stripped) Bir Proxy { #proxy-with-a-stripped-path-prefix }
Uygulamanıza bir path prefix ekleyen bir proxy'niz olabilir.
Bu durumlarda uygulamanızı yapılandırmak için `root_path` kullanabilirsiniz.
`root_path`, FastAPI'nin (Starlette üzerinden) üzerine kurulduğu ASGI spesifikasyonunun sağladığı bir mekanizmadır.
`root_path` bu özel senaryoları yönetmek için kullanılır.
Ayrıca sub-application mount ederken de içeride kullanılır.
Path prefix'i kırpılan bir proxy kullanmak, şu anlama gelir: Kodunuzda `/app` altında bir path tanımlarsınız; ancak üstte bir katman (proxy) ekleyip **FastAPI** uygulamanızı `/api/v1` gibi bir path'in altına koyarsınız.
Bu durumda, orijinal `/app` path'i aslında `/api/v1/app` altında servis edilir.
Kodunuzun tamamı sadece `/app` varmış gibi yazılmış olsa bile.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[6] *}
Proxy, request'i app server'a (muhtemelen FastAPI CLI üzerinden Uvicorn) iletmeden önce **path prefix**'i anlık olarak **"kırpar"** (strip). Böylece uygulamanız hâlâ `/app` altında servis ediliyormuş gibi davranır ve tüm kodunuzu `/api/v1` prefix'ini içerecek şekilde güncellemeniz gerekmez.
Buraya kadar her şey normal çalışır.
Ancak entegre doküman arayüzünü (frontend) açtığınızda, OpenAPI şemasını `/api/v1/openapi.json` yerine `/openapi.json` üzerinden almayı bekler.
Dolayısıyla tarayıcıda çalışan frontend `/openapi.json`'a erişmeye çalışır ve OpenAPI şemasını alamaz.
Çünkü uygulamamız proxy arkasında `/api/v1` path prefix'i ile çalışmaktadır; frontend'in OpenAPI şemasını `/api/v1/openapi.json` üzerinden çekmesi gerekir.
```mermaid
graph LR
browser("Browser")
proxy["Proxy on http://0.0.0.0:9999/api/v1/app"]
server["Server on http://127.0.0.1:8000/app"]
browser --> proxy
proxy --> server
```
/// tip | İpucu
`0.0.0.0` IP'si, genelde programın ilgili makine/server üzerindeki tüm kullanılabilir IP'lerde dinlediği anlamına gelir.
///
Docs UI'nin, bu API `server`'ının (proxy arkasında) `/api/v1` altında bulunduğunu belirtmek için OpenAPI şemasına da ihtiyacı olur. Örneğin:
```JSON hl_lines="4-8"
{
"openapi": "3.1.0",
// Burada daha fazla şey var
"servers": [
{
"url": "/api/v1"
}
],
"paths": {
// Burada daha fazla şey var
}
}
```
Bu örnekte "Proxy", **Traefik** gibi bir şey olabilir. Server da FastAPI uygulamanızı çalıştıran (Uvicorn'lu) FastAPI CLI olabilir.
### `root_path` Sağlama { #providing-the-root-path }
Bunu yapmak için `--root-path` komut satırı seçeneğini şöyle kullanabilirsiniz:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Hypercorn kullanıyorsanız, onda da `--root-path` seçeneği vardır.
/// note | Teknik Detaylar
ASGI spesifikasyonu bu kullanım senaryosu için bir `root_path` tanımlar.
`--root-path` komut satırı seçeneği de bu `root_path`'i sağlar.
///
### Mevcut `root_path`'i Kontrol Etme { #checking-the-current-root-path }
Uygulamanızın her request için kullandığı mevcut `root_path` değerini alabilirsiniz; bu değer ASGI spesifikasyonunun bir parçası olan `scope` dict'inin içindedir.
Burada sadece göstermek için bunu mesaja dahil ediyoruz.
{* ../../docs_src/behind_a_proxy/tutorial001_py310.py hl[8] *}
Ardından Uvicorn'u şu şekilde başlatırsanız:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
Response şöyle bir şey olur:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
### FastAPI Uygulamasında `root_path` Ayarlama { #setting-the-root-path-in-the-fastapi-app }
Alternatif olarak, `--root-path` gibi bir komut satırı seçeneği (veya muadili) sağlayamıyorsanız, FastAPI uygulamanızı oluştururken `root_path` parametresini ayarlayabilirsiniz:
{* ../../docs_src/behind_a_proxy/tutorial002_py310.py hl[3] *}
`FastAPI`'ye `root_path` vermek, Uvicorn veya Hypercorn'a `--root-path` komut satırı seçeneğini vermekle eşdeğerdir.
### `root_path` Hakkında { #about-root-path }
Şunu unutmayın: Server (Uvicorn) bu `root_path`'i, uygulamaya iletmek dışında başka bir amaçla kullanmaz.
Ancak tarayıcınızla <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a> adresine giderseniz normal response'u görürsünüz:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
Yani `http://127.0.0.1:8000/api/v1/app` üzerinden erişilmeyi beklemez.
Uvicorn, proxy'nin Uvicorn'a `http://127.0.0.1:8000/app` üzerinden erişmesini bekler; bunun üstüne ekstra `/api/v1` prefix'ini eklemek proxy'nin sorumluluğudur.
## Stripped Path Prefix Kullanan Proxy'ler Hakkında { #about-proxies-with-a-stripped-path-prefix }
Stripped path prefix kullanan bir proxy, yapılandırma yöntemlerinden yalnızca biridir.
Birçok durumda varsayılan davranış, proxy'nin stripped path prefix kullanmaması olacaktır.
Böyle bir durumda (stripped path prefix olmadan), proxy `https://myawesomeapp.com` gibi bir yerde dinler; tarayıcı `https://myawesomeapp.com/api/v1/app`'e giderse ve sizin server'ınız (ör. Uvicorn) `http://127.0.0.1:8000` üzerinde dinliyorsa, proxy (stripped path prefix olmadan) Uvicorn'a aynı path ile erişir: `http://127.0.0.1:8000/api/v1/app`.
## Traefik ile Local Olarak Test Etme { #testing-locally-with-traefik }
<a href="https://docs.traefik.io/" class="external-link" target="_blank">Traefik</a> kullanarak, stripped path prefix'li deneyi local'de kolayca çalıştırabilirsiniz.
<a href="https://github.com/containous/traefik/releases" class="external-link" target="_blank">Traefik'i indirin</a>; tek bir binary'dir, sıkıştırılmış dosyayı çıkarıp doğrudan terminalden çalıştırabilirsiniz.
Ardından `traefik.toml` adında bir dosya oluşturup şunu yazın:
```TOML hl_lines="3"
[entryPoints]
[entryPoints.http]
address = ":9999"
[providers]
[providers.file]
filename = "routes.toml"
```
Bu, Traefik'e 9999 portunda dinlemesini ve `routes.toml` adlı başka bir dosyayı kullanmasını söyler.
/// tip | İpucu
Standart HTTP portu 80 yerine 9999 portunu kullanıyoruz; böylece admin (`sudo`) yetkileriyle çalıştırmanız gerekmez.
///
Şimdi diğer dosyayı, `routes.toml`'u oluşturun:
```TOML hl_lines="5 12 20"
[http]
[http.middlewares]
[http.middlewares.api-stripprefix.stripPrefix]
prefixes = ["/api/v1"]
[http.routers]
[http.routers.app-http]
entryPoints = ["http"]
service = "app"
rule = "PathPrefix(`/api/v1`)"
middlewares = ["api-stripprefix"]
[http.services]
[http.services.app]
[http.services.app.loadBalancer]
[[http.services.app.loadBalancer.servers]]
url = "http://127.0.0.1:8000"
```
Bu dosya, Traefik'i `/api/v1` path prefix'ini kullanacak şekilde yapılandırır.
Ardından Traefik, request'leri `http://127.0.0.1:8000` üzerinde çalışan Uvicorn'unuza yönlendirir.
Şimdi Traefik'i başlatın:
<div class="termy">
```console
$ ./traefik --configFile=traefik.toml
INFO[0000] Configuration loaded from file: /home/user/awesomeapi/traefik.toml
```
</div>
Ve şimdi uygulamanızı `--root-path` seçeneğiyle başlatın:
<div class="termy">
```console
$ fastapi run main.py --forwarded-allow-ips="*" --root-path /api/v1
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
</div>
### Response'ları Kontrol Edin { #check-the-responses }
Şimdi Uvicorn'un portundaki URL'ye giderseniz: <a href="http://127.0.0.1:8000/app" class="external-link" target="_blank">http://127.0.0.1:8000/app</a>, normal response'u görürsünüz:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
/// tip | İpucu
`http://127.0.0.1:8000/app` üzerinden erişiyor olsanız bile, `root_path` değerinin `--root-path` seçeneğinden alınıp `/api/v1` olarak gösterildiğine dikkat edin.
///
Şimdi de Traefik'in portundaki URL'yi, path prefix ile birlikte açın: <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/app</a>.
Aynı response'u alırız:
```JSON
{
"message": "Hello World",
"root_path": "/api/v1"
}
```
ama bu sefer proxy'nin sağladığı prefix path olan `/api/v1` ile gelen URL'de.
Elbette buradaki fikir, herkesin uygulamaya proxy üzerinden erişmesidir; dolayısıyla `/api/v1` path prefix'li sürüm "doğru" olandır.
Uvicorn'un doğrudan sunduğu, path prefix olmayan sürüm (`http://127.0.0.1:8000/app`) ise sadece _proxy_'nin (Traefik) erişmesi için kullanılmalıdır.
Bu da Proxy'nin (Traefik) path prefix'i nasıl kullandığını ve server'ın (Uvicorn) `--root-path` seçeneğinden gelen `root_path`'i nasıl kullandığını gösterir.
### Docs UI'yi Kontrol Edin { #check-the-docs-ui }
Şimdi işin eğlenceli kısmı. ✨
Uygulamaya erişmenin "resmi" yolu, tanımladığımız path prefix ile proxy üzerinden erişmektir. Bu yüzden beklendiği gibi, Uvicorn'un doğrudan servis ettiği docs UI'yi URL'de path prefix olmadan açarsanız çalışmaz; çünkü proxy üzerinden erişileceğini varsayar.
Şuradan kontrol edebilirsiniz: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image01.png">
Ancak docs UI'yi proxy üzerinden, `9999` portuyla, `/api/v1/docs` altında "resmi" URL'den açarsak doğru çalışır! 🎉
Şuradan kontrol edebilirsiniz: <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a>:
<img src="/img/tutorial/behind-a-proxy/image02.png">
Tam istediğimiz gibi. ✔️
Bunun nedeni, FastAPI'nin OpenAPI içinde varsayılan `server`'ı, `root_path` tarafından verilen URL ile oluşturmak için bu `root_path`'i kullanmasıdır.
## Ek `server`'lar { #additional-servers }
/// warning | Uyarı
Bu daha ileri seviye bir kullanım senaryosudur. İsterseniz atlayabilirsiniz.
///
Varsayılan olarak **FastAPI**, OpenAPI şemasında `root_path` için bir `server` oluşturur.
Ancak başka alternatif `servers` da sağlayabilirsiniz; örneğin *aynı* docs UI'nin hem staging hem de production ortamıyla etkileşime girmesini istiyorsanız.
Özel bir `servers` listesi verirseniz ve bir `root_path` varsa (çünkü API'niz proxy arkasındadır), **FastAPI** bu `root_path` ile bir "server"ı listenin başına ekler.
Örneğin:
{* ../../docs_src/behind_a_proxy/tutorial003_py310.py hl[4:7] *}
Şöyle bir OpenAPI şeması üretir:
```JSON hl_lines="5-7"
{
"openapi": "3.1.0",
// Burada daha fazla şey var
"servers": [
{
"url": "/api/v1"
},
{
"url": "https://stag.example.com",
"description": "Staging environment"
},
{
"url": "https://prod.example.com",
"description": "Production environment"
}
],
"paths": {
// Burada daha fazla şey var
}
}
```
/// tip | İpucu
`url` değeri `/api/v1` olan, `root_path`'ten alınmış otomatik üretilen server'a dikkat edin.
///
Docs UI'de, <a href="http://127.0.0.1:9999/api/v1/docs" class="external-link" target="_blank">http://127.0.0.1:9999/api/v1/docs</a> adresinde şöyle görünür:
<img src="/img/tutorial/behind-a-proxy/image03.png">
/// tip | İpucu
Docs UI, seçtiğiniz server ile etkileşime girer.
///
/// note | Teknik Detaylar
OpenAPI spesifikasyonunda `servers` özelliği opsiyoneldir.
`servers` parametresini belirtmezseniz ve `root_path` `/` ile aynıysa, üretilen OpenAPI şemasında `servers` özelliği varsayılan olarak tamamen çıkarılır; bu da `url` değeri `/` olan tek bir server ile eşdeğerdir.
///
### `root_path`'ten Otomatik `server` Eklenmesini Kapatma { #disable-automatic-server-from-root-path }
**FastAPI**'nin `root_path` kullanarak otomatik bir server eklemesini istemiyorsanız, `root_path_in_servers=False` parametresini kullanabilirsiniz:
{* ../../docs_src/behind_a_proxy/tutorial004_py310.py hl[9] *}
Böylece OpenAPI şemasına dahil etmez.
## Bir Sub-Application Mount Etme { #mounting-a-sub-application }
Bir sub-application'ı ( [Sub Applications - Mounts](sub-applications.md){.internal-link target=_blank} bölümünde anlatıldığı gibi) mount etmeniz gerekiyorsa ve aynı zamanda `root_path` ile bir proxy kullanıyorsanız, bunu beklendiği gibi normal şekilde yapabilirsiniz.
FastAPI içeride `root_path`'i akıllıca kullanır; dolayısıyla doğrudan çalışır. ✨