Neden önem veriyoruz: Biz Geonode olarak proxy satışı yapıyoruz; bu nedenle kullanıcılar, bağlantıları, boyutları ve erişilebilirliği uygun maliyetle kontrol etmek için sürekli olarak bizim aracılığımızla HEAD istekleri gönderiyorlar. Dürüstçe belirtmek gerekirse, HEAD farklı bir istektir, hafif bir GET isteği değildir ve onu öyleymiş gibi ele almak, kesin olarak yanlış sonuçlara yol açar. HEAD isteğine 200 yanıtı veren bir URL, GET isteğine 403 yanıtı verebilir. HEAD isteğinde "Content-Length" bilgisi göstermeyen bir kaynak, GET isteğinde bu bilgiyi gösterebilir. Ayrıca bir bot önleme katmanı, olağandışı bir HEAD isteğini kendi sinyali olarak değerlendirebilir. HEAD, asıl amacına uygun olarak mükemmeldir; ancak "bunu gerçekten alsam ne olurdu" sorusuna cevap vermek için yetersiz bir göstergedir.
Doğru Yöntem
curl -I https://example.com
curl kılavuzunda -I, --head
adresi şu şekilde açıklanmaktadır: "(HTTP FTP FILE) Yalnızca başlıkları alır. HTTP sunucuları, bir belgenin yalnızca başlığını almak için kullanılan HEAD komutuna sahiptir. Bir FTP veya FILE URL'sinde kullanıldığında, curl yalnızca dosya boyutunu ve son değiştirilme zamanını görüntüler."
Çıktı:
HTTP/2 200
content-type: text/html; charset=UTF-8
content-length: 1256
last-modified: Thu, 17 Oct 2019 07:18:26 GMT
cache-control: max-age=604800
Başlıktaki sorunun cevabı tam olarak budur. Aşağıda ise zaman kazandıran kısım yer almaktadır.
Neden -X HEAD Yanlış?
curl kılavuzu, bu konuyu “-X, --request” başlığı altında doğrudan ele almaktadır:
Bu seçenek yalnızca HTTP isteğinde kullanılan kelimeyi değiştirir; curl’ün davranışını etkilemez. Örneğin, düzgün bir HEAD isteği yapmak istiyorsanız,
-X HEADkomutunu kullanmak yeterli değildir.--headseçeneğini kullanmanız gerekir.
İşleyiş şu şekildedir: -X yalnızca yöntem dizesini değiştirir, başka hiçbir şeyi değiştirmez. curl hâlâ bir GET isteği yapıyormuş gibi davranır; bu da hâlâ bir yanıt gövdesi beklediği anlamına gelir. HEAD'i doğru şekilde uygulayan sunucu, başlıkları gönderir ancak gövde göndermez. curl, asla gelmeyecek içeriği bekler ve zaman aşımı veya bağlantının kapanmasıyla sonlanana kadar komut donmuş gibi görünür.
Kılavuzda ayrıca, yönlendirmeler nedeniyle kullanıcıları yanıltan ikinci bir -X davranışı hakkında da uyarı bulunmaktadır: "--location kullanılırsa, --request ile ayarladığınız yöntem dizesi tüm istekler için kullanılır". Dolayısıyla -X POST -L, yönlendirme zincirindeki her bir durak için bir POST isteği yeniden gönderir; bu ise nadiren kimsenin istediği bir durumdur.
Kılavuzda da belirtildiği gibi genel ilke şudur: "Normalde bu seçeneğe ihtiyacınız yoktur. Her türlü GET, HEAD, POST ve PUT isteği, genellikle özel komut satırı seçenekleri kullanılarak çağrılır." HEAD için -I, POST için -d, PUT için -T kullanın ve -X adresini PROPFIND gibi gerçekten sıra dışı yöntemler için ayırın.
HEAD Yöntemi Aslında Nedir?
RFC 9110 §9.3.2, bunu tek bir cümleyle tanımlamaktadır:
HEAD yöntemi, sunucunun yanıtta içerik GÖNDERMEMESİ GEREKTİĞİ dışında GET yöntemiyle aynıdır.
Ayrıca amacını şöyle belirtir: "HEAD, genellikle hipermetin bağlantılarını test etmek veya son değişiklikleri bulmak amacıyla, seçilen temsilin temsil verilerini aktarmadan meta verilerini elde etmek için kullanılır."
Bu, sunucular için katı bir gerekliliktir — MUST NOT içerik göndermemelidir — ve curl'a içerik beklemesi söylendiğinde donmasının nedeni budur.
Başlık kuralı ise kasıtlı olarak daha esnektir ve insanların yanlış anladığı kısım da budur:
Sunucu, bir HEAD isteğine yanıt olarak, istek yöntemi GET olsaydı göndereceği başlık alanlarının aynısını GÖNDERMELİDİR. Ancak sunucu, değeri yalnızca içerik oluşturulurken belirlenen başlık alanlarını atlayabilir.
RFC somut bir örnek vermektedir: dinamik yanıtları arabelleğe alan sunucular, bir GET isteğinde "HEAD yanıtı içinde oluşturulmayan" Content-Length ve Vary adreslerini üretebilir. RFC, bunları “küçük tutarsızlıklar” olarak adlandırır ve “HEAD isteği genellikle verimlilik amacıyla yapıldığından, HEAD isteği için içeriği oluşturup sonra atmaya kıyasla bu durumun tercih edilebilir” olduğunu belirtir.
Dolayısıyla, bir HEAD isteğinde Content-Length adresinin eksik olması mutlaka bir hata değildir ve mutlaka anlamlı da değildir. Bu durum, sunucunun sayfayı işleyerek ancak öğrenebileceği bir şeyi hesaplamayı reddetmesinden ibaret olabilir.
Ayrıca, araçlar geliştiriyorsanız bilmeniz gereken istek gövdeleriyle ilgili bir kural daha vardır. HEAD isteğindeki içerik “genel olarak tanımlanmış bir anlam içermez, isteğin anlamını veya hedefini değiştiremez ve istek kaçakçılığı saldırısı potansiyeli nedeniyle bazı uygulamaların isteği reddetmesine ve bağlantıyı kapatmasına yol açabilir”. RFC’ye göre, önceden belirli bir anlaşma olmadığı sürece bir istemci “HEAD isteğinde içerik ÜRETMEMELİDİR”. HEAD isteğiyle birlikte istek gövdesi göndermeyin.
HEAD Komutunun Kullanım Alanları
Gerçekten yararlı durumlar; bunların hepsinde tam bir aktarım yerine birkaç yüz baytlık bir aktarım yapılır.
Bir URL'nin aktif olup olmadığını kontrol etmek:
curl -sI -o /dev/null -w '%{response_code}\n' https://example.com
İndirmeden önce bir dosyanın boyutunu öğrenmek:
curl -sI https://example.com/large.iso | grep -i content-length
Yönlendirme zincirini takip etmek ve raporlamak:
curl -sIL -o /dev/null -w '%{num_redirects} hops -> %{url_effective}\n' https://example.com
Bir sunucunun aralık isteklerini destekleyip desteklemediğini kontrol etmek; bu, kesintiye uğrayan bir indirmenin devam edip edemeyeceğini belirler:
curl -sI https://example.com/file.zip | grep -i accept-ranges
İndirmeden güncelliği kontrol etmek:
curl -sI https://example.com/data.json | grep -iE 'last-modified|etag'
Toplu bağlantı kontrolü, bu klasik bir kullanım şeklidir ve bant genişliği tasarrufunun katlanarak arttığı yerdir:
while read -r url; do
code=$(curl -sIL -o /dev/null -w '%{response_code}' --max-time 10 "$url")
echo "$code $url"
done < urls.txt
Ölçümlü bant genişliği kullanıldığında tasarruf gerçektir: URL başına 500 KB veri aktaracak bir bağlantı kontrolü, bunun yerine sadece birkaç yüz bayt aktarır. On bin URL için bu, beş gigabayt ile birkaç megabayt arasındaki fark demektir.
HEAD Sizi Yanıltıyorsa
Başlıkta yer alan uyarıya neden olan hata durumları.
Sunucu, HEAD isteğini tamamen reddediyor. GET isteğinin sorunsuz çalıştığı bir URL’de 405 Method Not Allowed
. Statik içeriklerde pek görülmez, ancak API’lerde ve uygulama uç noktalarında nadir değildir.
Sunucu, HEAD'i farklı şekilde işler. Farklı durum kodları, farklı başlıklar, bazen uygulamada tamamen farklı bir kod yolu. RFC, içerikten türetilen başlıkların atlanmasına izin verir ve uygulamalar bunu ne ölçüde uyguladıkları konusunda farklılık gösterir.
Önbellekler ve CDN'ler HEAD isteklerini ayrı olarak indeksleyebilir. Bir HEAD isteğindeki önbellek başlıkları, bir GET isteğinin bulacağı önbellek girdisinden farklı bir girdiyi yansıtabilir; bu nedenle HEAD, önbellekleme davranışını incelemek için güvenilir bir yol değildir.
Bot önleme sistemleri farklı şekilde yanıt verir. Tanıdık olmayan bir istemciden gelen bir HEAD isteği başlı başına bir işarettir ve aldığınız yanıt, tarayıcıdan gelen bir GET isteğinin alacağı yanıtla aynı olmayabilir.
Content-Length
'si eksik veya yanlış olabilir. Spesifikasyon tarafından izin verilir, dinamik içerikte yaygındır ve oluşturulan herhangi bir şeyin indirme boyutu tahmini için zayıf bir temeldir.
Yönlendirme zincirleri farklılık gösterebilir. Bazı sunucular, özellikle içerik müzakeresi söz konusu olduğunda, GET ve HEAD isteklerini farklı yerlere yönlendirir.
Gerçek bir isteğin ne yapacağını bilmeniz gerektiğinde, gerçek bir istek gönderin ve gövdeyi atın:
curl -sS -o /dev/null -D - https://example.com
Gerçek bir GET isteği, başlıklar stdout'a yazılır, gövde atılır. Bant genişliği bedelini ödersiniz ve doğru bir yanıt alırsınız. İkisinden bilinçli bir şekilde seçim yapın: Ucuz bir çözüm istiyorsanız -I
, doğru bir sonuç istiyorsanız -o /dev/null -D -
.
Büyük kaynaklar için bir orta yol daha vardır — kaynağın tamamı yerine bir bayt isteyin:
curl -sS -r 0-0 -o /dev/null -D - https://example.com/large.iso
-r, --range
"bir bayt aralığı (yani kısmi bir belge)" alır, dolayısıyla 0-0
yalnızca ilk baytı getirir. Bu, neredeyse hiç bant genişliği maliyeti olmadan, gerçek GET davranışına sahip gerçek bir GET isteğidir. Kılavuzdaki uyarı: "Birçok HTTP/1.1 sunucusunda bu özellik etkin değildir", bu nedenle önce Accept-Ranges: bytes
adresini kontrol edin ve bu adresin mevcut olmaması durumunda tam bir yanıt bekleyin.
Komut Dosyalarına Kopyalamaya Değer Örnekler
Yukarıdaki komutlar, etraflarına biraz yapı eklenmesiyle çok daha kullanışlı hale gelir.
Dürüst raporlama yapan bir bağlantı denetleyicisi. Basit versiyon, 200 dışında her yanıtı bozuk bağlantı olarak değerlendirir; bu da yönlendirmelerde ve HEAD isteğini reddeden sunucularda yanlış uyarılar üretir. Bu örnek ise bunları birbirinden ayırır:
check() {
local url="$1" code
code=$(curl -sIL -o /dev/null --max-time 10 -w '%{response_code}' "$url")
case "$code" in
200) echo "OK $url" ;;
405) code=$(curl -sSL -o /dev/null --max-time 10 -w '%{response_code}' "$url")
echo "GET:$code $url" ;;
000) echo "TIMEOUT $url" ;;
*) echo "$code $url" ;;
esac
}
405 dalı önemlidir: HEAD isteğini reddeden bir sunucu, bozuk bir bağlantı değildir ve bunu anlamanın tek yolu GET olarak yeniden denemektir. 000, curl'ün hiç HTTP yanıtı gelmediğini bildirme yöntemidir; bu, ağ hatasını sunucu hatasından ayırır.
Paralellik, dikkatli olun. Bağlantı kontrolü utanç verici derecede paraleldir ve bunu geniş çapta çalıştırma eğilimi vardır. Buna direnin:
xargs -P 8 -I{} sh -c 'check "$1"' _ {} < urls.txt
Sekiz, makul bir varsayılan değerdir. Buradaki sınır, kendi kapasitenizden ziyade başkasının sunucusuna aşırı yük bindirmenin nezaket sınırlarıdır; ayrıca, hız sınırlama olayına neden olan bir bağlantı denetleyicisi, tasarruf ettirdiğinden daha fazla maliyete yol açar.
Her zaman bir zaman aşımı süresi belirleyin. Yanıt vermeyen bir ana bilgisayara yapılan HEAD isteği, tam olarak bir GET isteği kadar uzun süre takılır. --max-time 10 ile --connect-timeout 5 bu süreyi sınırlar ve binlerce URL üzerinde bir döngüde bu sınır, işin tamamlanmasını sağlar.
Sadece durumu değil, etkin URL’yi de kaydedin. -L adresinden sonra %{url_effective} komutunu çalıştırmak, bağlantının gerçekte nereye yönlendirildiğini gösterir; bu da “bu bağlantı çalışıyor” ifadesini “bu bağlantı çalışıyor ve artık başka bir yere yönlendiriyor” haline getirir — genellikle bu, daha ilginç bir bulgudur.
Sonuçlarınızı önbelleğe alın. Her çalıştırmada her URL’yi yeniden kontrol etmek bant genişliğini ve iyi niyeti boşa harcar. Durumu ve ETag veya Last-Modified adreslerini saklayın ve sonraki geçişlerde koşullu istekler kullanın; böylece değişmemiş kaynaklar için tam bir kontrol yerine 304 adresi kullanılır.
Proxy Aracılığıyla HEAD
Değişen üç husus var ve hepsini bilmekte fayda var.
curl -I -x http://user:pass@proxy.example.com:9000 https://example.com
HTTPS için önce “CONNECT” (HEAD) istekleri gerçekleşir. curl, HEAD isteğinden önce bir tünel kurar ve ayrıntılı çıktıda, proxy’nin buna verdiği yanıt hedefin yanıtından önce görünür. --suppress-connect-headers bunu gizler; %{http_connect} ise proxy’nin durumunu hedefin durumundan ayrı olarak bildirir.
Önemli olan bant genişliğinden tasarruf etmektir. Trafik ölçümü uygulandığında, bir HEAD isteği tam bir sayfanın kapladığı alana kıyasla sadece birkaç yüz baytlık bir maliyet oluşturur. Bağlantı doğrulama, kullanılabilirlik izleme ve büyük hacimli boyut kontrolleri söz konusu olduğunda, bu durum işin uygun maliyetli mi yoksa pahalı mı olacağını belirleyen faktördür. Bu, gigabayt başına fiyatlandırmada mevcut olan birkaç gerçek anlamda büyük optimizasyondan biridir.
Ancak engellemeler ve doğrulama istekleri farklı davranır. Bir GET isteğine doğrulama sayfası sunan bir bot önleme katmanı, bir HEAD isteğini reddedebilir veya tam tersi de olabilir. Hedefe erişilip erişilemediğini kontrol etmek için HEAD kullanıyorsanız, tüm listeye güvenmeden önce bir örnek üzerinde gerçek bir GET ile sonucu doğrulayın. Bu, proxy'leri test etmenin önemi başlıklı yazımızda bahsettiğimiz "sessiz hata" örneğidir: istek başarılı olur, yanıt yanlıştır ve bunu size bildiren hiçbir şey yoktur.
Bilmeniz gereken bir curl detayı: -G, --get komutu, --head ile birleştirilebilir. Kılavuzda, -G komutunun --head ile birlikte kullanılması durumunda, “POST verilerinin bunun yerine bir HEAD isteğinde URL’ye ekleneceği” belirtilmektedir — bu, bir HEAD isteğinde anahtar-değer çiftlerinden oluşturulan sorgu parametrelerine ihtiyacınız olduğunda kullanışlıdır.
Yanıtı Doğru Şekilde Okuma
Geri gelen yanıtlardan en iyi şekilde yararlanma.
Önce durum. 200
mevcut. 301
/302
taşındı — takip etmek için -L
adresini ekleyin. 403
reddedildi. 404
artık yok. 405
, sunucunun HEAD yöntemini kabul etmediği anlamına gelir; bu durumda GET olarak yeniden deneyin. 429
, yavaşlamanız gerektiği anlamına gelir.
**Content-Length
** varsa, spesifikasyonun bunu atlamaya izin verdiğini unutmayın.
**Accept-Ranges: bytes
**, indirmenin devam ettirilebileceği ve aralık isteklerinin kullanılabileceği anlamına gelir.
**Last-Modified
ve ETag
**, koşullu istekleri etkinleştirir. -z
, If-Modified-Since
gönderir — kılavuzda bu, "belirtilen tarih ve saatten sonra değiştirilmiş bir dosya" talep etmek olarak açıklanır — ve --etag-compare
, ETag
tarafını yönetir. Bir 304 Not Modified
neredeyse hiçbir maliyeti yoktur ve bir kaynağı tekrar tekrar sorgulamak için doğru yoldur.
**Content-Type
** size ne alacağınızı gösterir. JSON beklediğiniz yerde text/html
çıkması genellikle bir hata veya oturum açma sayfası anlamına gelir.
Makine tarafından kullanılacaksa, metin ayrıştırma işlemini tamamen atlayın:
curl -sI -o /dev/null -w '%{header_json}' https://example.com | jq
%{header_json}
, tüm yanıt başlıklarını küçük harfli adlar ve dizi değerleriyle JSON olarak gönderir; bu, tekrarlanan başlıkları doğru şekilde işler ve ayrıştırıcıya olan ihtiyacı ortadan kaldırır. Bu konuyu ve diğer inceleme seçeneklerini curl ile yanıt başlıklarını gösterme başlıklı yazımızda ele aldık.
Sık Sorulan Sorular
curl ile HEAD isteği nasıl gönderilir?
curl -I https://example.com. Tam biçimi şöyledir: --head. -X HEAD komutunu kullanmayın — kılavuzda, bu komutun düzgün bir HEAD isteği için “yeterli olmadığı” açıkça belirtilmiştir; çünkü bu komut yalnızca yöntem dizesini değiştirirken, curl bir yanıt gövdesi beklemeye devam eder.
curl -X HEAD neden takılıyor?
Çünkü -X komutu yalnızca istek satırındaki kelimeyi değiştirir, curl'ün davranışını değiştirmez. RFC 9110, bir sunucunun HEAD yanıtında "içerik GÖNDERMEMESİ GEREKTİĞİNİ" belirtir; bu nedenle sunucu doğru bir şekilde yanıt gövdesi göndermezken, curl yine de yanıt gövdesini bekler. Bunun yerine -I komutunu kullanın.
HEAD ile GET arasındaki fark nedir?
HEAD, sunucunun gövde göndermemesi gerektiği dışında GET ile aynıdır. İçerik aktarımı yapmadan meta verileri elde etmek için kullanılır; genellikle bağlantı kontrolü veya güncelliği test etmek amacıyla kullanılır. Sunucular, GET için gönderdikleriyle aynı başlıkları göndermelidir, ancak yalnızca içerik oluşturulurken hesaplanan başlıkları atlayabilir.
HEAD her zaman GET ile aynı başlıkları döndürür mü?
Hayır. Spesifikasyona göre sunucular aynı başlıkları göndermelidir (SHOULD), ancak "yalnızca içerik oluşturulurken değeri belirlenen" başlıkları atlayabilir (MAY) — örnek olarak Content-Length ve Vary başlıklarını vermektedir. Spesifikasyon, bu küçük tutarsızlıkların, gövde oluşturup sonra atmaya tercih edilebilir olduğunu belirtir.
Bir dosyayı indirmeden dosya boyutunu nasıl öğrenebilirim?
curl -sI URL | grep -i content-length. Dinamik olarak oluşturulan içeriklerde bu başlığın bulunmayabileceğini unutmayın; spesifikasyon buna izin vermektedir. Neredeyse hiçbir maliyet olmadan daha güvenilir bir yanıt almak için, -r 0-0 komutuyla tek bir bayt isteyin ve Content-Range başlığını okuyun.
Bir URL tarayıcıda neden çalışıyor da curl -I komutuna 405 hatası veriyor?
Çünkü sunucu o uç noktada HEAD isteğini kabul etmiyor. 405 Method Not Allowed, GET isteklerini sorunsuz işleyen bir sunucudan HEAD isteğine verilen geçerli bir yanıttır. Gövdeyi atlayarak GET olarak yeniden deneyin: curl -sS -o /dev/null -D - URL.
Gövde içeren bir HEAD isteği gönderebilir miyim?
Göndermemelisiniz. RFC 9110'a göre, HEAD isteğindeki içeriğin "genel olarak tanımlanmış bir anlamı yoktur", isteğin anlamını değiştiremez ve "istek kaçakçılığı saldırısı potansiyeli nedeniyle bazı uygulamaların isteği reddetmesine ve bağlantıyı kapatmasına yol açabilir". İstemciler, HEAD isteğinde içerik oluşturmamalıdır.
HEAD, bir proxy'nin çalışıp çalışmadığını kontrol etmek için yararlı mıdır?
Kısmen. Bağlantıyı doğrular ve durum kodunu hızlı bir şekilde döndürür; bu da iyi bir hızlı testtir. Gerçek bir GET isteğinin başarılı olup olmayacağını göstermez, çünkü bot önleme katmanları genellikle bu ikisini farklı şekilde ele alır. Bir liste genelinde HEAD sonuçlarına güvenmeden önce, bir örnek üzerinde gerçek GET istekleriyle doğrulama yapın.
Sonuç
Bu konuyu tamamen iki komut kapsıyor. Düzgün bir HEAD isteği için curl -I URL, gerçek bir GET isteğinin üreteceği başlıkları almak istediğinizde ise curl -sS -o /dev/null -D - URL. Çalışmayan komut ise -X HEAD’dir; kılavuzda da açıkça belirtildiği gibi: bu komut sadece kelimeyi değiştirir, davranışı değiştirmez; dolayısıyla curl, sunucunun göndermemesi gereken bir gövdeyi bekler.
Karar, bu ikisinden hangisini istediğinizdir. HEAD, çok daha az kaynak tüketir — tam bir sayfaya karşılık birkaç yüz bayt — bu da onu, herhangi bir hacimde ve özellikle de bant genişliği sınırlamalı bağlantılarda bağlantı kontrolü, kullanılabilirlik izleme ve boyut tahmini için doğru araç haline getirir. Ancak, gerçek bir getişin ne döndüreceğini tahmin etmek için yanlış bir araçtır; çünkü sunucuların içerikten türetilen başlıkları atlamasına izin verilir, HEAD'i doğrudan reddedebilirler ve sıklıkla farklı bir mantıkla yönlendirebilirler.
Bant genişliği kısıtlaması olmadan GET'in doğruluğuna ihtiyacınız varsa, -r 0-0 az kullanılan bir orta yoldur: bir bayt alan gerçek bir GET isteğidir. Önce Accept-Ranges: bytes adresini kontrol edin, çünkü birçok sunucu size zaten dosyanın tamamını verecektir.
