iframe “bağlanmayı reddetti” hatası: X-Frame-Options çözümü

Önemli bilgi

Bir iframe içindeki “bağlanmayı reddetti” mesajı bir ağ hatası değildir. Gömülen site, tarayıcınıza kendisini başka bir sayfanın içinde göstermemesini söyleyen bir X-Frame-Options ya da CSP frame-ancestors başlığı gönderdi. Çözüm, gömülen sitenin sunucusundadır; iframe etiketinizde asla: tüm sitede SAMEORIGIN'i koruyun ve tek bir gömme yolunu frame-ancestors * ile açın.

Şimdi kontrol edin

X-Frame-Options açıklaması: “bağlanmayı reddetti” gösteren bir iframe ve çözümü — tüm sitede SAMEORIGIN, gömme yolunda CSP frame-ancestors *
X-Frame-Options açıklaması: “bağlanmayı reddetti” gösteren bir iframe ve çözümü — tüm sitede SAMEORIGIN, gömme yolunda CSP frame-ancestors *
Okumaya başlayın
  1. Bir iframe içindeki “bağlanmayı reddetti” gerçekte ne demek?
  2. X-Frame-Options nasıl çalışır?
  3. DENY mi SAMEORIGIN mi: hangisini göndermeli?
  4. ALLOW-FROM öldü — kullanmayın
  5. frame-ancestors: Onun yerini alan CSP yönergesi
  6. İki başlık da ayarlıysa ne olur?
  7. Adım 1: Sizi engelleyen başlığı bulun
  8. Adım 2: Sayfayı kimin çerçeveleyebileceğine karar verin
  9. Doğru kalıp: Siteyi kilitleyin, tek bir gömme yolunu açın
  10. Nginx (ve add_header tuzağı)
  11. Apache
  12. Next.js ve Express
  13. Üçüncü taraf widget'ı güvenle gömme
  14. sandbox: Hesap makinesi widget'ı için nelere izin verilmeli
  15. Gömen tarafta neden çözüm yok?
  16. Her değişiklikten sonra başlıkları doğrulayın
  17. Belirti → Neden → Çözüm
  18. Sıkça Sorulan Sorular

Bir iframe içindeki “bağlanmayı reddetti” gerçekte ne demek?

Başka bir siteye işaret eden bir <iframe> ekliyorsunuz, sayfayı yeniliyorsunuz ve sayfa yerine üzgün yüz simgeli gri bir kutu görüyorsunuz; üzerine gelince example.com bağlanmayı reddetti. yazıyor. İlk tepkiniz ağ sorunundan şüphelenmek oluyor, çünkü Chrome ölü bir sunucu için tam olarak bunu gösterir. Ama DevTools'u açın; konsol farklı bir hikâye anlatıyor:

text
Refused to display 'https://emicalcs.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.

Ağda hiçbir şey başarısız olmadı. DNS çözüldü, TCP bağlantısı açıldı, TLS tamamlandı ve sunucu gayet düzgün bir 200 döndürdü. Tarayıcı sayfayı indirdi, tek bir yanıt başlığını okudu ve sonra sayfayı sizin sayfanızın içinde çizmeyi bilerek reddetti. Bu başlık X-Frame-Options'tır ve clickjacking'i engellemek için vardır: kötü niyetli bir sayfanın görünmez bir iframe içine bir giriş ya da ödeme formu yükleyip ziyaretçileri göremedikleri düğmelere tıklamaya kandırdığı saldırı.

Bu, sorunu nasıl gidereceğiniz açısından önemlidir. Gerçek bir bağlantı hatası — URL'yi doğrudan bir sekmede açtığınızda ERR_CONNECTION_REFUSED — sunucu ya da güvenlik duvarı sorunudur ve ERR_CONNECTION_REFUSED yazısında ele alınır. iframe sürümü yalnızca sayfa bir çerçeve içinde olduğunda görünür ve aynı URL kendi sekmesinde sorunsuz çalışır. Site doğrudan yükleniyor ama çerçevenizde yüklenmiyorsa karşınızdaki şey bir kesinti değil, bir çerçeveleme politikasıdır.

Bir sayfanın X-Frame-Options ve CSP başlıklarını görün

X-Frame-Options nasıl çalışır?

X-Frame-Options, ilk kez RFC 7034'te tanımlanan bir HTTP yanıt başlığıdır; bu bilgilendirici RFC'nin yerini artık HTML standardı almıştır ve Internet Explorer 8'den beri tüm tarayıcılar destekler. Sunucu bunu bir sayfaya ekler; tarayıcı işlemeden önce okur ve sayfanın başka bir sayfadaki <frame>, <iframe>, <embed> ya da <object> içinde görünüp görünemeyeceğine karar verir.

Hâlâ önemli olan iki değeri ve artık önemi kalmamış bir değeri alır:

DeğerSayfayı kim çerçeveleyebilirTipik kullanım
DENYHiç kimse — aynı site bileGiriş sayfaları, yönetim panelleri, oturum taşıyan her şey
SAMEORIGINYalnızca tam olarak aynı kökene (şema + ana makine + port) ait sayfalarÇoğu site için makul varsayılan; kendi sayfalarınızı çerçevelemenize izin verir
ALLOW-FROM https://a.comEski — tüm modern tarayıcılar yok sayarKullanmayın; aşağıya bakın

Başlık site başına değil, yanıt başına geçerlidir. Çoğu rehberin atladığı ve bu yazıdaki çözümü mümkün kılan ayrıntı budur: bir sunucu sayfalarının %99'unda SAMEORIGIN gönderirken, gömülmek üzere tasarlanmış tek yolda farklı bir politika gönderebilir.

DENY mi SAMEORIGIN mi: hangisini göndermeli?

Bir nedeniniz yoksa SAMEORIGIN gönderin. Üçüncü taraf çerçevelerin hepsini engeller, kendi sitenizin kendi sayfalarını çerçevelemesine ise izin verir — yardım widget'ları, önizlemeler, başka iç sayfaları yükleyen paneller.

Çerçevelemenin meşru bir amacı olmayan ve zararın ağır olacağı sayfalarda DENY gönderin: giriş formları, ödeme sayfaları, “hesabı sil” gibi tek tıkla yapılan işlemler. DENY ayrıca ince bir durumu da kapsar: bir saldırganın kendi sayfalarınızdan birine çerçeve enjekte etmeyi başarması — SAMEORIGIN ile bu enjekte edilmiş çerçeve yine de izinli olurdu.

Hangisini seçerseniz seçin, hata sayfaları ve statik dosyalar dahil her yanıtı kapsaması için web sunucusu ya da CDN düzeyinde bir kez tanımlayın. Uygulama kodunda ayarlanan başlıklar 404 ve bakım sayfasını kaçırmaya eğilimlidir; bunları tüm yanıtların geçtiği yerde ayarlayın.

ALLOW-FROM öldü — kullanmayın

Eski yazılar hâlâ tek bir gömen siteye izin vermek için X-Frame-Options: ALLOW-FROM https://partner.com önerir. Chrome ve Safari'de hiç çalışmadı, Firefox ise sürüm 70'te (Ekim 2019) kaldırdı. Daha kötüsü, ALLOW-FROM gören modern bir tarayıcı başlığın tamamını geçersiz sayar ve tümüyle yok sayar; bu da sayfayı herkesin çerçeveleyebileceği hâle getirir — niyetinizin tam tersi.

Belirli sitelere izin vermeniz gerekiyorsa cevap, sonraki bölümde anlatılan CSP frame-ancestors'tır. X-Frame-Options yalnızca “hiç kimse” ya da “sadece ben” diyebilir.

frame-ancestors: Onun yerini alan CSP yönergesi

Content Security Policy Seviye 2, X-Frame-Options'ın yaptığı her şeyi yapan ve onun yapamadığı şeyi ekleyen bir yönerge olan frame-ancestors'ı getirdi: gerçek bir izin listesi. Standart Content-Security-Policy başlığının içinde taşınır ve bir ya da daha fazla kaynak kabul eder:

http
# Nobody may frame this (same as X-Frame-Options: DENY)
Content-Security-Policy: frame-ancestors 'none'

# Only my own origin (same as SAMEORIGIN)
Content-Security-Policy: frame-ancestors 'self'

# My origin plus two named partners, one with a subdomain wildcard
Content-Security-Policy: frame-ancestors 'self' https://partner.example https://*.trusted.example

# Anyone may frame this — for a public embed endpoint only
Content-Security-Policy: frame-ancestors *

Geçiş yapmadan önce bilmeye değer üç ayrıntı:

  • Yalnızca ebeveyni değil, tüm atalarını kontrol eder. A sayfası B'yi, B de sizin sayfanızı çerçeveliyorsa hem A hem B'nin izinli olması gerekir (CSP). HTML standardı artık X-Frame-Options'ı da tüm atalara uygular (WHATWG), ancak eski tarayıcılar yalnızca ebeveyni ya da en üst sayfayı kontrol ederdi.
  • Yalnızca HTTP başlığı olarak çalışır. CSP bir <meta http-equiv> etiketiyle iletildiğinde frame-ancestors açıkça yok sayılır — tarayıcının belgeyi işleyip işlemeyeceğine karar vermeden önce politikayı bilmesi gerekir. Aynısı X-Frame-Options için de geçerlidir. İkisi de HTML içinden ayarlanamaz.
  • Joker karakterler ana makine başınadır, genel değil. https://*.example.com tek bir sitenin herhangi bir alt alan adıyla eşleşir; çıplak bir * internetteki her kökenle eşleşir. Bir hesap makinesi widget'ı için uygun, oturum taşıyan her şey için yanlış.

İki başlık da ayarlıysa ne olur?

Çoğu site geçiş sırasında ikisini birden göndermeye başlar ve öncelik tanımlıdır: frame-ancestors varsa tarayıcı onu uygular ve X-Frame-Options'ı yok sayar. CSP Seviye 2 şartnamesi bunu açıkça söyler; Chrome, Firefox, Safari ve Edge de buna uyar.

Bu, güvenli geçiş yolunu basitleştirir. CSP 2'den önceki birkaç çok eski istemci için X-Frame-Options: SAMEORIGIN'i koruyun, yanına Content-Security-Policy: frame-ancestors 'self' ekleyin; tüm modern tarayıcılar CSP kuralını kullanır. İkisi yalnızca farklı şeyler söylemelerini sağlarsanız sorun çıkarır — örneğin X-Frame-Options: DENY ile frame-ancestors * yan yana — o durumda modern tarayıcılar çerçevelemeye izin verir ve yalnızca çok eski olanlar engeller. Tutarlı tutarsanız hata ayıklanacak bir şey kalmaz.

Adım 1: Sizi engelleyen başlığı bulun

Bir şeyi değiştirmeden önce hangi politikanın devrede olduğunu doğrulayın. Konsol başlığın adını zaten söyledi; curl ise sunucunun gönderdiği tam değeri gösterir. Bu örnek, aşağıdaki uygulamalı örnekteki siteyi kullanıyor — ana sayfaları bilerek kilitli:

bash
curl -sI https://emicalcs.com/ | grep -i -E 'x-frame-options|content-security-policy'

# Output:
# x-frame-options: SAMEORIGIN

Üç şeyden biri döner:

  • x-frame-options: SAMEORIGIN ya da DENY — klasik çerçeveleme politikası. Yalnızca o sunucudaki bir değişiklik bunu kaldırabilir.
  • content-security-policy: … frame-ancestors … — bir CSP politikası. Kaynak listesini okuyun; kökeniniz orada yoksa engellenmişsiniz demektir. Yönerge çoğunlukla script-src ve benzerlerinin yanında uzun bir CSP başlığının içine gömülüdür; bu yüzden değerin tamamını okuyun.
  • İki başlık da yok: gömülen site çerçeveye izin veriyor ve “bağlanmayı reddetti” başka bir nedenden kaynaklanıyor: kilitli olan bir sayfaya yönlendirme (çoğu zaman bir giriş sayfası; curl -sIL ile izleyin) ya da kendi sayfanızdaki, katılmayan her çerçeveyi engelleyen ve aynı gri kutuyu gösteren bir Cross-Origin-Embedder-Policy: require-corp başlığı.

Adım 2: Sayfayı kimin çerçeveleyebileceğine karar verin

Bundan sonrası tek bir soruya bağlı: çerçevelenen siteyi siz mi yönetiyorsunuz?

Hayırsa burada durun. İstemci tarafında bir çözüm yok — ne bir sandbox özniteliği, ne farklı bir src, ne de ziyaretçilerinizin sahip olacağı bir tarayıcı ayarı. Site sahibi sayfasının gömülmemesine karar verdi ve tarayıcı bu kararı onun adına uyguluyor. Seçenekleriniz sayfaya bağlantı vermek, sahibine bir gömme uç noktası sunup sunmadığını sormak ya da varsa resmî bir widget kullanmaktır. Ana sitesini kilitleyen birçok hizmet bir tane sunar; sonraki bölümdeki örnek bunun nasıl göründüğünü gösteriyor.

Siteyi siz yönetiyorsanız başlığı tüm siteden kaldırma isteğine direnin. Bu, tek bir sayfadaki gömmeyi düzeltmek için oturum ve form taşıyanlar dahil her sayfada clickjacking'i yeniden açar. Bunun yerine hangi belirli yolların çerçevelenmesi gerektiğine ve kim tarafından çerçevelenmesi gerektiğine karar verin:

DurumO yolda gönderilecek başlık
Kendi panonuz tarafından çerçevelenen iç araçframe-ancestors 'self' (artı X-Frame-Options: SAMEORIGIN)
Birkaç bilinen ortak tarafından gömülen widgetframe-ancestors 'self' https://partner-a.com https://partner-b.com
Herkese açık gömülebilir widget (hesap makinesi, harita, oynatıcı)yalnızca gömme yolunda frame-ancestors *
Geri kalan her şeyframe-ancestors 'self' artı X-Frame-Options: SAMEORIGIN

Doğru kalıp: Siteyi kilitleyin, tek bir gömme yolunu açın

Bu kalıbın karşılaştığım en temiz gerçek örneği, diğer sitelerin gömmesine davet edildiği, kredi taksiti, SIP, gelir vergisi, GST gibi 45 Hint finans hesap makinesinden oluşan ücretsiz bir koleksiyon olan EMICalcs. Ana site kilitli; özel bir /embed/ yolu açık. İkisinin başlıklarını karşılaştırın:

bash
# The calculator page a visitor normally uses — locked
curl -sI https://emicalcs.com/income-tax-calculator/ | grep -i -E 'x-frame|frame-ancestors'
# x-frame-options: SAMEORIGIN

# The same calculator on its embed path — open to any site
curl -sI https://emicalcs.com/embed/income-tax-calculator/ | grep -i -E 'x-frame|frame-ancestors'
# content-security-policy: frame-ancestors *

Gömme yanıtının neyi içermediğine dikkat edin: /embed/ üzerinde hiç X-Frame-Options başlığı yok. Bu kasıtlı. CSP yönergesi öncelikli olduğu için SAMEORIGIN'i bırakmak modern tarayıcılarda zararsız olurdu — ama başlıkları okuyacak bir sonraki mühendisin kafasını karıştırır ve yalnızca X-Frame-Options anlayan birkaç eski istemciyi yine de engellerdi. Gömme yolu tek bir politika gönderir ve o politika tek bir şey söyler.

Sitenin geri kalanı SAMEORIGIN'i korur; böylece ana sayfa, makale sayfaları ve tam hesap makinesi sayfaları clickjacking'e karşı korunmuş kalır. Gömme sürümleri gezinmesiz ve ele geçirmeye değer hiçbir şey içermeyen sadeleştirilmiş belgelerdir; bu yüzden onları *'a açmak makul bir takastır.

Kendi widget'ınızı yayımlıyorsanız onların gömme rehberi bir şablon olarak okumaya değer: hesap makinesi başına tek bir kalıcı URL, çoğuna uyan sabit bir yükseklik, kod parçasında loading="lazy" ve emekli edilen gömme URL'lerinin 404 vermek yerine yönlendirileceği sözü — çünkü sizi gömen her site URL'nizin çalışmaya devam edeceğine güveniyor.

Nginx (ve add_header tuzağı)

Çoğu sitenin başlık ayarladığı üç yerde aynı kalıp. Her durumda site genelindeki bir kural her şeyi kilitler, daha dar bir kural gömme yolunda onu geçersiz kılar. Bir tuzağı olduğu için önce Nginx:

nginx
# Site-wide default (server block)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
add_header X-Content-Type-Options "nosniff" always;

location /embed/ {
    # WARNING: any add_header here stops ALL add_header inheritance from the
    # server block — re-declare every header you still want on this path.
    add_header Content-Security-Policy "frame-ancestors *" always;
    add_header X-Content-Type-Options "nosniff" always;
}

Yorum satırı isteğe bağlı okuma değil. add_header seviyeler arasında birleştirilmez: bir location bloğu herhangi bir add_header tanımladığı anda server bloğundaki tüm add_header'ları miras almayı bırakır. Ekipler gömme yolunda HSTS ya da nosniff başlıklarını böyle kaybeder ve aylar sonra bir güvenlik taramasında fark eder. İhtiyacınız olanları yeniden tanımlayın ve yolun tek bir net politika göndermesi için X-Frame-Options'ı gömme bloğunun dışında bırakın. nginx 1.29.3'ten beri add_header_inherit merge mirası geri açabilir (nginx), ama gömme bloğunda kullanmayın: site genelindeki X-Frame-Options ve frame-ancestors 'self' başlıklarını o yola geri ekler ve iki CSP başlığı olduğunda tarayıcı ikisini de uyguladığı için çerçeve engellenmiş kalır.

Apache

Apache'nin Header yönergesi kapsamlar arasında birleşir; bu yüzden varsayılanı bir kez tanımlayıp gömme yolunda değişen yalnızca iki başlığa dokunabilirsiniz:

apache
# Site-wide default (needs mod_headers)
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Content-Security-Policy "frame-ancestors 'self'"

<LocationMatch "^/embed/">
    Header always unset X-Frame-Options
    Header always set Content-Security-Policy "frame-ancestors *"
</LocationMatch>

Next.js ve Express

Next.js'te başlıklar next.config.js içinde tanımlanır. İki kural aynı yolla eşleşip aynı başlığı ayarladığında sonraki kural kazanır — ancak yalnızca önceki kuralın ayarladığı bir başlık yine de uygulanır. X-Frame-Options'ı gömme yolundan tamamen uzak tutmak için onu site genelindeki kuraldan negatif ileri bakışla (negative lookahead) hariç tutun:

javascript
// next.config.js
module.exports = {
  async headers() {
    return [
      {
        // Every path EXCEPT /embed/... gets the locked defaults
        source: '/((?!embed/).*)',
        headers: [
          { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
          { key: 'Content-Security-Policy', value: "frame-ancestors 'self'" },
        ],
      },
      {
        source: '/embed/:path*',
        headers: [
          { key: 'Content-Security-Policy', value: 'frame-ancestors *' },
        ],
      },
    ]
  },
}

Express'te popüler helmet ara yazılımı varsayılan olarak zaten X-Frame-Options: SAMEORIGIN ve frame-ancestors 'self' içeren bir CSP gönderir. Onu genel olarak bağlayın, ardından gömme router'ında res.removeHeader('X-Frame-Options') çağırın ve işlemeden önce Content-Security-Policy'yi frame-ancestors * olarak ayarlayın. Sıra önemlidir: geçersiz kılmanız helmet'ten sonra çalışmalı, yoksa helmet onu ezer.

Üçüncü taraf widget'ı güvenle gömme

Şimdi çitin öbür tarafı: gömen site sizsiniz, sağlayıcının açık bir gömme yolu var ve çerçevenin düzgün davranmasını istiyorsunuz. İyi yapılmış bir widget kod parçası şöyle görünür; EMICalcs'in her hesap makinesi sayfasında yayımladığı budur:

html
<iframe src="https://emicalcs.com/embed/income-tax-calculator/"
        title="Income Tax Calculator FY 2026-27 — EMICalcs"
        width="100%" height="760"
        style="max-width:680px;border:0;border-radius:16px;overflow:hidden"
        loading="lazy"></iframe>
<p>Free <a href="https://emicalcs.com/income-tax-calculator/" target="_blank" rel="noopener">Income Tax Calculator FY 2026-27</a> by EMICalcs</p>

Her özniteliğin bir görevi var:

  • title — çerçeveye ekran okuyucular için ad verir. Onsuz yardımcı teknoloji yalnızca “çerçeve” der ve erişilebilirlik denetimleri sayfayı başarısız sayar.
  • style içinde max-width ile width="100%" — mobilde dar sütunları doldurur, masaüstünde geniş sütunlardan taşmaz.
  • Sabit bir height — gömen sayfa, başka kökenden bir çerçevenin içerik yüksekliğini okuyamaz; bu yüzden sabit bir değer, yüklenirken yerleşim kaymasını önlemenin tek yoludur. Widget başına bir kez ayarlayın.
  • loading="lazy" — isteği çerçeve görünür alana yaklaşana kadar erteler; böylece uzun bir makalenin sonundaki widget ilk yüklemede hiçbir şeye mal olmaz.
  • border:0; border-radius; overflow:hidden — varsayılan çökük kenarlığı kaldırır ve köşeleri keser; widget sayfanıza özgü görünür.

Kredi satırı bilerek iframe'in dışında durur. Çerçevenin içindeki her şey gömülen belgeye aittir — bağlantıları sağlayıcının bağlantılarıdır, sağlayıcının URL'sindedir — bu yüzden çerçeve içindeki bir bağlantı, okurlarınız ya da bir arama motoru tarayıcısı için sayfanızın parçası değildir. Kaynağa atıf yapmak istiyorsanız ya da sağlayıcı istiyorsa yukarıdaki gibi, kendi HTML'inizde çerçevenin yanına düz bir paragraf koyun.

sandbox: Hesap makinesi widget'ı için nelere izin verilmeli

sandbox özniteliği gömen tarafın kendi korumasıdır. Değersiz eklerseniz çerçevelenen sayfa hiçbir şey alamaz: betik yok, form yok, açılır pencere yok, pencerenizi yönlendirme yok ve kendi çerezlerini ve depolamasını devre dışı bırakan opak bir köken. Sonra yalnızca widget'ın ihtiyacı olanı geri ekleyin:

html
<iframe src="https://emicalcs.com/embed/loan-emi-calculator/"
        sandbox="allow-scripts allow-forms allow-same-origin allow-popups"
        title="Loan EMI Calculator — EMICalcs"
        width="100%" height="760" loading="lazy"></iframe>

allow-scripts ve allow-forms, etkileşimli her şey için asgari düzeydir. allow-same-origin, çerçevelenen sayfanın kendi kökenini korumasına izin verir; böylece depolaması ve çerezleri çalışır — başka kökenden bir gömmede bu güvenlidir, çünkü “onun kökeni” hâlâ sizin değil sağlayıcınındır. allow-popups yalnızca widget bağlantıları yeni sekmede açıyorsa gerekir; EMICalcs gömmesindeki kredi bağlantısının yaptığı gibi.

Yapmamanız gereken şey, kendi kökeninize işaret eden bir çerçevede allow-scripts ile allow-same-origin'i birleştirmektir. Çerçevelenen betik o zaman yukarı çıkıp kendi sandbox özniteliğini kaldırabilir ve hiçbir şeyi yalıtmamış olursunuz.

Gömen tarafta neden çözüm yok?

“refused to connect iframe” aramasında üç popüler geçici çözüm çıkar. Hepsi kötü ve nedenini tam olarak bilmek yararlı.

  • X-Frame-Options'ı silen tarayıcı eklentileri — yalnızca kendi tarayıcınızda çalışır. Sitenizin her ziyaretçisi hâlâ gri kutuyu görür ve bu arada ziyaret ettiğiniz her sitede clickjacking korumasını kapatmış olursunuz.
  • Sayfayı kendi sunucunuzdan geçirmek — üçüncü tarafın HTML'ini sunucu tarafında alıp kendi kökeninizden sunarsınız; böylece başlık tarayıcıya hiç ulaşmaz. Bu, göreli bağlantıları, çerezleri, girişleri ve betikle çalışan her şeyi bozar; sizi onların içeriğinden sorumlu kılar; ve genellikle sağlayıcının şartlarını ihlal eder. Sizi yakalayan siteler sunucunuzun IP'sini engeller.
  • Ekran görüntüsü ya da “web'den görsele” hizmetleri — çalışan bir sayfa değil, statik bir resim alırsınız. Önizleme küçük resmi için uygun, hesap makinesi için işe yaramaz.

Başlık, site sahibinin sayfasına ne olabileceğine dair, tarayıcı tarafından uygulanan bir beyanıdır. Meşru yollar daha önce ele alınan ikisidir: sahibi bir gömme yolu açar ya da siz bağlantı verirsiniz. Gömülmek isteyen sağlayıcılar bunu kolaylaştırır — iyileri bir kod parçası yayımlar, URL'yi kalıcı tutar ve yalnızca açması güvenli olan yolu açar.

Her değişiklikten sonra başlıkları doğrulayın

Çerçeveleme başlıkları bir yapılandırma dosyasında ayarlanıp unutulur; bir CDN, bir ters proxy ya da iyi niyetli bir güvenlik eklentisi bunları istediği an ekleyebilir, silebilir ya da çoğaltabilir. Yayına aldıktan sonra ağınızın dışından kontrol edin:

bash
# The locked pages should show SAMEORIGIN and/or frame-ancestors 'self'
curl -sI https://example.com/ | grep -i -E 'x-frame-options|content-security-policy'

# The embed path should show ONLY frame-ancestors, with the sources you intended
curl -sI https://example.com/embed/widget/ | grep -i -E 'x-frame-options|content-security-policy'

# Error pages should carry the header too
curl -sI https://example.com/this-does-not-exist | grep -i -E 'x-frame-options|content-security-policy'

Sonra bir tarayıcının yaptığı gibi test edin: gömme URL'sini farklı bir kökenden sunulan bir sayfadaki <iframe> içine koyun — yerel bir file:// sayfası işe yaramaz, çünkü tarayıcılar onu özel olarak ele alır — ve konsolu açın. Temiz bir yükleme, Refused to display ya da Refused to frame satırının olmaması demektir.

Site Cloudflare ya da başka bir CDN'in arkasındaysa önbellekteki bir kopya veya bir uç kuralı, kaynağınızdan farklı başlıklar taşıyabilir. HTTP Başlık Kontrolü URL'yi tarayıcınızın ve ağınızın dışından, sunucumuzdan ister ve X-Frame-Options ile Content-Security-Policy'yi bir çerçeveleme kararıyla (SAMEORIGIN ya da DENY, CSP frame-ancestors üzerinden ya da gönderilmedi) gösterir: gömme yolunuza sessizce SAMEORIGIN'i geri ekleyen bir CDN kuralını yakalamanın hızlı yolu.

Belirti → Neden → Çözüm

Rehberin tamamını yeniden okumak yerine doğrudan çözüme atlamak için bunu kullanın:

Ne görüyorsunuzNe anlama geliyorÇözüm
Gri kutuda “bağlanmayı reddetti”, ama URL bir sekmede yükleniyorX-Frame-Options ya da frame-ancestors çerçeveyi engellediYalnızca site sahibi değiştirebilir — onun gömme yolunu kullanın ya da bağlantı verin
Konsol: Refused to display … 'X-Frame-Options' to 'sameorigin'Klasik başlık; yalnızca aynı kökenden çerçevelere izin varÇerçevelenen sitede gömme yolunu frame-ancestors * ya da bir ortak listesiyle açın
Konsol: Refused to frame … violates … frame-ancestorsCSP izin listesi kökeninizi içermiyorKökeninizi çerçevelenen sitenin frame-ancestors kaynaklarına ekleyin
Dün çalışıyordu, bugün bozuk, kod değişikliği yokBir CDN ya da güvenlik eklentisi SAMEORIGIN enjekte etmeye başladıcurl ile kontrol edin; CDN ya da eklenti kuralında gömme yolunu hariç tutun
Kendi panonuz kendi sayfanızı çerçeveleyemiyorAlt alan adı ya da şema uyuşmazlığı (www ve kök alan adı, http ve https)frame-ancestors 'self' https://example.com https://*.example.com kullanın (joker karakter çıplak alan adını kapsamaz)
Gömme çalışıyor ama o yolda HSTS ya da nosniff kaybolduNginx add_header mirası bunları düşürdülocation bloğunun içinde her başlığı yeniden tanımlayın
URL'yi doğrudan açınca ERR_CONNECTION_REFUSEDGerçek bir ağ hatası, çerçeveleme politikası değilERR_CONNECTION_REFUSED yazısına ve port kontrolcüsüne bakın

URL kendi sekmesinde de başarısız oluyorsa çerçeve yanıltıcı bir ipucudur. Herhangi bir çerçeveleme başlığına dokunmadan önce ağ katmanlarını sırayla — DNS, port, TLS, başlıklar — herkese açık API uç noktası test rehberiyle gözden geçirin.

Bir URL'nin gönderdiği tüm güvenlik başlıklarını görün

Herhangi bir sayfa için X-Frame-Options, Content-Security-Policy, HSTS ve gerisini kontrol edin; sunucularımızdan alındığı için bir tarayıcı eklentisi ya da kendi ağınızdaki bir proxy gördüklerinizi değiştiremez. Ücretsiz, kayıt gerekmez.

Kaynaklar

  1. RFC 7034: HTTP Header Field X-Frame-Options, IETF
  2. CSP Level 3: frame-ancestors, W3C
  3. HTML Standard: the X-Frame-Options header, WHATWG
  4. ngx_http_headers_module (add_header, add_header_inherit), nginx

Sıkça Sorulan Sorular

X-Frame-Options eskidi mi?

İşlevsel olarak yerini, iki başlık da varken tarayıcıların öncelik verdiği CSP frame-ancestors yönergesi aldı. Yine de işe yaramaz değil: güncel tüm tarayıcılar frame-ancestors yokken X-Frame-Options'ı hâlâ dikkate alır ve CSP Seviye 2'den önceki az sayıdaki eski istemciyi korur. Pratik öneri ikisini de göndermek ve tutarlı tutmaktır — frame-ancestors 'self' yanında SAMEORIGIN.

X-Frame-Options: DENY ne demek?

DENY, tarayıcıya sayfanın aynı sitedeki sayfalar dahil hiçbir sayfada frame, iframe, embed ya da object öğesi içinde asla işlenmemesi gerektiğini söyler. En katı ayardır ve sayfanın gömülmesi için meşru bir neden olmayan, clickjacking'in ciddi olacağı giriş formları, ödeme sayfaları ve yönetim panelleri için doğru olandır.

DENY ile SAMEORIGIN arasındaki fark nedir?

DENY tüm çerçevelemeyi engeller. SAMEORIGIN yalnızca tam olarak aynı kökene — aynı şema, ana makine ve port — ait sayfaların çerçevelemesine izin verir, diğer herkesi engeller. SAMEORIGIN site genelinde makul varsayılandır, çünkü üçüncü taraf clickjacking'i durdururken sitenizin kendi sayfalarını çerçevelemesine izin verir; DENY hiç çerçevelenmemesi gereken bir avuç sayfa içindir.

Bir siteyi gömmek için X-Frame-Options'ı nasıl aşarım?

Gömen taraftan aşamazsınız. Başlık, çerçevelemeye çalıştığınız site tarafından ayarlanır ve ziyaretçilerinizin tarayıcıları tarafından uygulanır; hiçbir iframe özniteliği, sandbox bayrağı ya da istemci tarafı numarası bunu değiştirmez. Başlığı silen eklentiler yalnızca kendi tarayıcınızı etkiler, sayfayı sunucunuzdan geçirmek ise sayfayı bozar ve genellikle sağlayıcının şartlarını ihlal eder. Meşru yollar, sağlayıcı sunuyorsa resmî bir gömme uç noktası kullanmak ya da sayfaya bağlantı vermektir.

X-Frame-Options'ı DENY olarak nasıl ayarlarım?

Hata sayfaları dahil tüm sayfaları kapsaması için web sunucusu düzeyinde bir HTTP yanıt başlığı olarak ekleyin. Nginx'te: add_header X-Frame-Options "DENY" always; Apache'de: Header always set X-Frame-Options "DENY". Modern tarayıcılar için Content-Security-Policy: frame-ancestors 'none' ile eşleştirin. Meta etiketiyle ayarlanamaz: yalnızca bir HTTP başlığı işe yarar.

Site yeni sekmede çalışırken iframe'im neden “bağlanmayı reddetti” diyor?

Çünkü bağlantıda bir sorun yok. Site sayfayı normal döndürdü, ancak sayfanın başka bir sayfa içinde işlenmesini yasaklayan bir X-Frame-Options ya da frame-ancestors başlığıyla. Chrome bunun için gerçek bir ağ hatasında gösterdiği aynı gri “bağlanmayı reddetti” kutusunu gösterir; kafa karıştıran da budur. “Refused to display … in a frame” konsol mesajı ele verendir.

X-Frame-Options ya da frame-ancestors'ı meta etiketinde ayarlayabilir miyim?

Hayır. İkisi de HTTP yanıt başlığı olarak iletilmelidir. Tarayıcının belgeyi işleyip işlemeyeceğine karar vermeden önce çerçeveleme politikasını bilmesi gerekir ve meta etiketi çok geç gelir. frame-ancestors, meta http-equiv CSP'sinde açıkça yok sayılır; bir meta etiketindeki X-Frame-Options'ı ise tüm tarayıcılar yok sayar.

frame-ancestors * sitemi güvensiz yapar mı?

Yalnızca onu gönderen yanıtlar için. Başlık belge başına değerlendirilir; bu yüzden özel bir /embed/ yolunda frame-ancestors * göndermek sitenin geri kalanını SAMEORIGIN ya da frame-ancestors 'self' ile tamamen korunmuş bırakır. Risk, bir saldırganın gömme sayfasının kendisini çerçeveleyerek kazanabileceğiyle sınırlıdır — oturum çerezi ve hassas işlemi olmayan durumsuz bir widget için bu hiçbir şeydir.

X-Frame-Options: ALLOW-FROM neden çalışmıyor?

ALLOW-FROM yalnızca Internet Explorer, orijinal Edge (12'den 18'e kadar sürümler) ve sürüm 70'te kaldıran Firefox tarafından uygulandı. Chrome ve Safari onu hiç desteklemedi. Modern tarayıcılar ALLOW-FROM içeren bir başlığı geçersiz sayar ve tümüyle yok sayar; bu da sayfayı herkesin çerçeveleyebileceği hâle getirir. İzin verilen kökenlerin gerçek bir listesini destekleyen CSP frame-ancestors yönergesini kullanın.

X-Frame-Options ve frame-ancestors ikisi de ayarlıysa hangi başlık kazanır?

frame-ancestors. HTML standardı, uygulanan bir CSP frame-ancestors içerdiğinde tarayıcılara X-Frame-Options'ı yok saymalarını söyler (CSP Seviye 2 bunu zaten öneriyordu) ve güncel tüm tarayıcılar böyle yapar; Report-Only politikası sayılmaz. Yalnızca CSP'yi anlamayacak kadar eski istemciler X-Frame-Options'a başvurur; bu yüzden göndermeye hâlâ değer — iki başlık uyumlu olduğu sürece.

Yazar hakkında

Shaik Vahid

DNS Robot'un kurucusu. Vahid, yıllarca web hosting desteğinde müşterilerin alan adı, DNS kaydı, SSL sertifikası ve e-posta teslimi sorunlarını çözdü. DNS Robot'un araçlarını geliştiriyor ve bu rehberleri insanların gerçekten karşılaştığı hatalardan yola çıkarak yazıyor.

HTTP Başlık Kontrolü

HTTP yanıt başlıklarını ve güvenlik başlıkları derecelendirmesini analiz edin