diff --git a/data-access/engine/src/vlkb/src/ast.cpp b/data-access/engine/src/vlkb/src/ast.cpp
index 45fccb186854171be3137b62769752c9026d83b8..537afb65888903fabb8369053ccf9bd013db03e0 100644
--- a/data-access/engine/src/vlkb/src/ast.cpp
+++ b/data-access/engine/src/vlkb/src/ast.cpp
@@ -113,36 +113,36 @@ void read_lines(string pathname, vector<string>& lines)
 	}
 }
 
-int header_backup(const string& pathname, const string& extra_cards_pathname, bool backup)
+int header_backup(const string& pathname, int extnum, const string& extra_cards_pathname, bool backup, std::ostream& ostrm)
 {
 	LOG_trace(__func__);
 
+   LOG_STREAM << pathname << " " << extnum << endl;
+
 	vector<string> extra_cards;
 	read_lines(extra_cards_pathname, extra_cards);
 
-	int maxHdu = 1;//FIXME INT_MAX; // read all HDU's
+	int maxHdu = extnum+1;
+	int minHdu = extnum+1;
 	std::vector<fitsfiles::Hdu> allHdus =
-		fitsfiles::fname2hdrstr(pathname, extra_cards, maxHdu);
+		fitsfiles::fname2hdrstr(pathname, extra_cards, maxHdu, minHdu);
 
 	for(unsigned int i=0; i<allHdus.size(); i++)
 	{
-		cerr << "HDU#" << i << endl;
-
 		fitsfiles::Hdu hd = allHdus.at(i);
 
 		if(backup)
 			write_previous(hd.m_header, pathname +"hdr" + ((i>0) ? "#" + to_string(i+1)  : "") );
 		else
 		{
-			unsigned long i = 0;
+			unsigned long ii = 0;
 			unsigned long hdr_len = hd.m_header.length();
-			while((i*80+80) <= hdr_len)
+         LOG_STREAM << "header card count: " << hdr_len/80 << endl;
+			while((ii*80+80) <= hdr_len)
 			{
-				cout << hd.m_header.substr(80*i++, 80) << "<" << endl;
+				ostrm << hd.m_header.substr(80*ii++, 80) << endl;
 			}
 		}
-		// FIXME remove all explicit cout cerr to main.cpp and here use ostream&
-
 	}
 
 	return 0;
diff --git a/data-access/engine/src/vlkb/src/ast.hpp b/data-access/engine/src/vlkb/src/ast.hpp
index 15571698efe130a51d5a3927a8317b3331edd3bc..ac5b9b3b76adb8ab716e488a62852217bdd3ba8a 100644
--- a/data-access/engine/src/vlkb/src/ast.hpp
+++ b/data-access/engine/src/vlkb/src/ast.hpp
@@ -5,10 +5,11 @@
 
 #include <string>
 #include <vector>
+#include <iostream>
 
 int vlkb_skyvertices(const std::string& pathname, const std::string& skysys_str);
 int vlkb_listbounds(const std::string& skysys_str, const std::string& specsys_str, const std::string& pathname);
-int header_backup(const std::string& pathname, const std::string& extra_cards_pathname, bool backup = false);
+int header_backup(const std::string& pathname, int extnum, const std::string& extra_cards_pathname, bool backup = false, std::ostream& ostrm=std::cout);
 int vlkb_overlap(const std::string& pathname, const std::string& region, std::vector<uint_bounds>& bnds);
 
 #endif
diff --git a/data-access/engine/src/vlkb/src/main.cpp b/data-access/engine/src/vlkb/src/main.cpp
index 7da51fb482d4f7537c8f1851723f7b758972ac7a..0bad88945305a4d18ee7b92dea74050caa6b4266 100644
--- a/data-access/engine/src/vlkb/src/main.cpp
+++ b/data-access/engine/src/vlkb/src/main.cpp
@@ -404,7 +404,7 @@ int cmd_header(int argc, char * argv[])
    if (argc < 2)
    {
       std::cerr
-         << "Usage:  header [--backup] [--extra-cards-pathname=<pathname>] <pathname.fits>...\n"
+         << "Usage:  header [--backup] [--extra-cards-pathname=<pathname>] [--extnum=] <pathname.fits>...\n"
          << "\n"
          << "Prints current header (one card per line) or writes the header into a file with the same pathname but 'fitshdr' extension.\n"
          << "Arguments:\n"
@@ -415,6 +415,7 @@ int cmd_header(int argc, char * argv[])
    {
       bool backup = false;
 		string extra_cards_pathname;
+      int extnum=0;
 
       for(int i=1; i<argc; i++)
       {
@@ -425,15 +426,16 @@ int cmd_header(int argc, char * argv[])
          else if(0 == (string(argv[i]).substr(0,2+21)).compare("--extra-cards-pathname="))
          {
             extra_cards_pathname = (string{argv[i]}).substr(2+21);
-				cout << "DBG " << extra_cards_pathname << endl;
+         }
+         else if(0 == (string(argv[i]).substr(0,2+7)).compare("--extnum="))
+         {
+            extnum = stoi( (string{argv[i]}).substr(2+7) );
          }
          else
          {
             string pathname(argv[i]);
-            cout << to_string(i) << " : " << pathname << endl;
 
-            rc = header_backup(pathname, extra_cards_pathname, backup);
-            std::cout << "header_backup rc: " << rc << std::endl;
+            rc = header_backup(pathname, extnum, extra_cards_pathname, backup, cout);
          }
       }
       rc = EXIT_SUCCESS;
diff --git a/data-access/servlet/src/main/java/cutout/Soda.java b/data-access/servlet/src/main/java/cutout/Soda.java
index 8410d7519fb62bd9b4869000f19dcdbce885e669..d113c3216aefb3affd02433f93f851c8ef38831a 100644
--- a/data-access/servlet/src/main/java/cutout/Soda.java
+++ b/data-access/servlet/src/main/java/cutout/Soda.java
@@ -21,5 +21,8 @@ public interface Soda
    public int doStream(String relPathname, int hdunum, String pixels,
          OutputStream outputStream) throws IOException, InterruptedException;
 
+   public int doStreamHeader(String relPathname, int hdunum,
+         OutputStream outputStream) throws IOException, InterruptedException;
+
 }
 
diff --git a/data-access/servlet/src/main/java/cutout/SodaImpl.java b/data-access/servlet/src/main/java/cutout/SodaImpl.java
index 44b460e05da1eca3b34e212f0ef113b9aa27c619..5b182c2fd1cc9bf01d8f25a030dba54c4d645367 100644
--- a/data-access/servlet/src/main/java/cutout/SodaImpl.java
+++ b/data-access/servlet/src/main/java/cutout/SodaImpl.java
@@ -146,5 +146,44 @@ class SodaImpl implements Soda
 	}
 
 
+   public int doStreamHeader(String relPathname, int hdunum,
+         OutputStream outputStream)  throws IOException, InterruptedException
+   {
+      Instant start = Instant.now();
+      LOGGER.fine("trace");
+
+		if(outputStream == null)
+			LOGGER.finest("supplied response outputStream is null");
+
+      final boolean isAbsPath= relPathname.startsWith("/");
+      String absPathname = isAbsPath ? relPathname : (fitsPaths.surveys() +"/"+ relPathname);
+
+		String[] cmd = new String[4];
+		cmd[0] = "/usr/local/bin/vlkb";
+		cmd[1] = "header";
+		cmd[2] = "--extnum=" + String.valueOf(hdunum-1);
+		cmd[3] = absPathname;
+		LOGGER.finest(String.join(" ", cmd));
+
+		StringBuilder errorStrBuilder = new StringBuilder();
+
+		ExecCmd exec = new ExecCmd();
+		exec.doRun(outputStream, cmd, errorStrBuilder);
+		int rc = exec.exitValue;
+		LOGGER.finest("exec header exitValue: " + rc);
+		LOGGER.finer("EXECTIME    headerDone: " + Duration.between(start, Instant.now()));
+
+		if(rc == 0)
+		{
+          return rc; // OK
+		}
+		else
+		{
+			throw new IllegalArgumentException("header could not be retrieved (rc="
+                   + rc + ") " + errorStrBuilder.toString());
+		}
+	}
+
+
 }
 
diff --git a/data-access/servlet/src/main/java/cutout/webapi/ServletCutout.java b/data-access/servlet/src/main/java/cutout/webapi/ServletCutout.java
index 4db0f6ad5b6e84da322317f3a965ac0168b0faa1..841f05e215f5fc383d23f30fdefe49bbcbe466ad 100644
--- a/data-access/servlet/src/main/java/cutout/webapi/ServletCutout.java
+++ b/data-access/servlet/src/main/java/cutout/webapi/ServletCutout.java
@@ -126,13 +126,22 @@ public class ServletCutout extends HttpServlet
 
       final Soda soda = new SodaImpl(settings.fitsPaths);
 
+      // if only ID given return header
+      boolean headerReq = (id!=null)
+                       && (pos==null) && (band==null) && (time==null) && (pol==null)
+                       && (pixels==null);
+
       resolver.resolve(id);
 
-      if(pixels != null)
-         return soda.doStream(resolver.relPathname(), resolver.hdunum(), pixels, respOutputStream);
+      if(headerReq)
+         return soda.doStreamHeader(resolver.relPathname(), resolver.hdunum(),
+                                                      respOutputStream);
+      else if(pixels != null)
+         return soda.doStream(resolver.relPathname(), resolver.hdunum(),
+                                                      pixels, respOutputStream);
       else
          return soda.doStream(resolver.relPathname(), resolver.hdunum(),
-																				pos, band, time, pol, respOutputStream);
+																		pos, band, time, pol, respOutputStream);
    }
 
 
@@ -269,13 +278,6 @@ public class ServletCutout extends HttpServlet
             Pol    pol  = Pol.parsePol(params);
             String pixels = SingleStringParam.parseSingleStringParam(params, "PIXELS");
 
-            // disable complete file/hdu download
-            if( (id!=null) && (pos==null) && (band==null) && (time==null) && (pol==null)
-                && (pixels==null))
-				{
-               throw new IllegalArgumentException("At least one of filtering args must be given.");
-				}
-
             String respFormat = sodaReq_getResponseFormat(request, DEFAULT_RESPONSEFORMAT);
 
             LOGGER.finest("responseFormat: " + respFormat);