# How to include arbitrary tags in the head of HTML documents

**URL:** <https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301>\
**Category:** Help!\
**Created:** [February 19, 2019, 1:26pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301 "2019-02-19T13:26:08Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![mye](https://avatars.discourse-cdn.com/v4/letter/m/dec6dc/32.png) [@mye](https://swi-prolog.discourse.group/u/mye)\
**Post date:** [February 19, 2019, 1:26pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301/1 "2019-02-19T13:26:09Z")

</div>

I’m trying to put an \<esi:include src=…/\> tag into my `<head>` section, so I defined this expander first:

```
html_write:expand(esi(Handler)) -->
     { http_link_to_id(Handler, [], HREF) },
    "<esi:include src=\"", [HREF], "\"/>".

```

Then I modify the ‘sheets’ page style to include this in the header

```
user:head(sheets, Head) -->
    html([ \html_receive(head),
           Head,
           esi(shared_head_esi),
           meta(two)
         ]).

```

I render the page like this:

```
sheets_homepage(Request) :-
    reply_html_page(
        sheets,
        title('Sheets'),
        [ \header_fragment(Request),
          \body_fragment(Request),
          \footer_fragment(Request)]).

```

The result is that the esi-tag is put into the body-tag and wraps everything in there, which doesn’t make sense. (BTW: the expander always creates `<esi:...></esi:...>` tag pairs but I just need a single `<esi: .../>` tag. Luckily the upstream processor seems to handle it. How do I generate single tags?)

I experimented a bit and it seems to me that the library throws out the tags it doesn’t want in the head!?! If so, how do I overwrite this behavior? How else could this be done?

I tried debugging but it does’t enter into the expansion process (presumably it’s written in C?).

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [February 19, 2019, 2:47pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301/2 "2019-02-19T14:47:31Z")

</div>

The expand/2 seems popular. It isn’t intended for that purpose. The way to get things into the HTML head is to use html\_post//2, I think this might do:

```
html_post(head, 'esi:include'(src(URL), []))

```

Not sure whether `:` is passed correctly. Is it even legal without using the old XHTML? If that fails you can opt for (not tested)

```
html_post(head, \["<esi:include src=\"...\"/>"])

```

The `\[...]` construct passes arbitrary strings to the output and can be used as an emergency to generate invalid HTML.

Next, to generate uniform pages, have a look at `reply_html_page/3`. That allows specifying a page style and a hook that provides all the page decoration depending on this style.

---

<div class="post-metadata">

**Author:** ![mye](https://avatars.discourse-cdn.com/v4/letter/m/dec6dc/32.png) [@mye](https://swi-prolog.discourse.group/u/mye)\
**Post date:** [February 19, 2019, 4:13pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301/3 "2019-02-19T16:13:17Z")

</div>

I have rewritten my code to use html\_post(head…), but it still puts the `<esi:include>...` at the beginning of body:

```
esi(URL) -->
    html_post(head, 'esi:include'(src(URL), [])).

sheets_homepage(Request) :-
    http_link_to_id(shared_head_esi, [], URL),
    reply_html_page(
        sheets,
        [ \esi(URL),
          title('Sheets')
        ],
        [ \header_fragment(Request),
          \body_fragment(Request),
          \footer_fragment(Request)]).

```

This results in

```
<html><head><title>Sheets</title>
</head><body><esi:include src="/esi/shared_head"></esi:include>
<header>
<div>LOGO</div>
<div>abc</div>
<footer>baz</footer>
</body></html>

```

I also tried with

```
esiraw(URL) -->
    { format(atom(ESI), "<esi:include src=\"~s\"/>", [URL]) },
    html_post(head, \[ESI]).

```

which give me the same result.

Where am I supposed to call the http\_post? I also called it from the `user:head` hook which didn’t output the tag at all. Then I also tried with a different topic for the receive:

```
user:head(sheets, Head) -->
    { http_link_to_id(shared_head_esi, [], URL) },
    html_post(esi, 'esi:include'(src(URL), [])),
    html(Head).

sheets_homepage(Request) :-
    reply_html_page(
        sheets,
        [ title('Sheets'),
          \html_receive(esi)
        ], [...

```

This also puts it into the `<body>`.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [February 19, 2019, 4:32pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301/4 "2019-02-19T16:32:49Z")

</div>

If I read this correctly, you are posting from the head to the head. That apparently goes wrong. If you use the 2nd argument of reply\_html\_page/3, just putting a term `'esi:include'(src(URL), [])` in the same list as the title element should be fine. If you use the user:head route, you just need to insert the element there, so I think this should work:

```auto
user:head(sheets, Head) -->
    { http_link_to_id(shared_head_esi, [], URL) },
    html('esi:include'(src(URL), [])),
    html(Head).

```

Note that this is all written in Prolog, but library(http/html\_write) sets `generate_debug_info` to `false` as tracing inside gets really confusing. You can comment that line in the library and debug as normal.

---

<div class="post-metadata">

**Author:** ![mye](https://avatars.discourse-cdn.com/v4/letter/m/dec6dc/32.png) [@mye](https://swi-prolog.discourse.group/u/mye)\
**Post date:** [February 19, 2019, 5:05pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301/5 "2019-02-19T17:05:57Z")

</div>

Unfortunately nothing I do works. I have prepared a minimal test case:

```
:- use_module(library(http/thread_httpd)).
:- use_module(library(http/http_dispatch)).
:- http_handler(root(.), sheets_homepage, []).

esi_server(Port) :-
    http_server(http_dispatch, [port(Port)]).

sheets_homepage(_Request) :-
    reply_html_page(
        sheets,
        [ title('Sheets'),
          html(meta(foo)), % This is included correctly
          % These are never included, they get pushed to the body.
          'esi:include'(src('/shared_head_esi'), []),
          html('esi:include'(src('/shared_head_esi'), []))
        ], [span(foo)]).

```

Note that the `meta` tag written by the library

`<meta http-equiv="content-type" content="text/html; charset=UTF-8">`

also gets pushed into the body if any “invalid” head tag is introduced. It’s like the library encounters an unknown header tag and then says to itself: “Yup that’s it, lets move on to the body”.

---

<div class="post-metadata">

**Author:** ![mye](https://avatars.discourse-cdn.com/v4/letter/m/dec6dc/32.png) [@mye](https://swi-prolog.discourse.group/u/mye)\
**Post date:** [February 19, 2019, 5:11pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301/6 "2019-02-19T17:11:35Z")

</div>

It occured to me that the raw-include might work here but now the `meta` tag by the library is correctly put in the head. But the esi tag now wraps the complete body!?!

```
esiraw(URL) -->
    { format(atom(ESI), "<esi:include src=\"~s\"/>", [URL]) },
    html( \[ESI]).

sheets_homepage(_Request) :-
    reply_html_page(
        sheets,
        [ title('Sheets'),
          \esiraw('/shared_head_esi')
        ], [span(foo)]).
```

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [February 19, 2019, 5:37pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301/7 "2019-02-19T17:37:01Z")

</div>

This works for me:

```
sheets_homepage(_Request) :-
    reply_html_page(
        sheets,
        [ title('Sheets'),
          meta([], []),
          'esi:include'(src('/shared_head_esi'), [])
        ], [span(foo)]).

```

Note that html is a predicate. You don’t want that in the head, as that would create an html element.

---

<div class="post-metadata">

**Author:** ![mye](https://avatars.discourse-cdn.com/v4/letter/m/dec6dc/32.png) [@mye](https://swi-prolog.discourse.group/u/mye)\
**Post date:** [February 19, 2019, 5:48pm UTC](https://swi-prolog.discourse.group/t/how-to-include-arbitrary-tags-in-the-head-of-html-documents/301/8 "2019-02-19T17:48:23Z")

</div>

OK, this is embarrassing!

All this time I was looking at the DOM in the the browser inspector and THAT moves the invalid `<head>` tags into the body. 🤦‍♂️ 🤣 **Lesson** : use “View source” during development!

Thank you for helping me through this.
