diff --git a/config2.h.in b/config2.h.in
index 8e5d0a653..b0b932deb 100644
--- a/config2.h.in
+++ b/config2.h.in
@@ -34,9 +34,6 @@
 /* Define if you want: codepoint lookup */
 #mesondefine CONFIG_CODEPOINT
 
-/* Define if you want: Combining characters support */
-#mesondefine CONFIG_COMBINE
-
 /* Define if you want: Cookies support */
 #mesondefine CONFIG_COOKIES
 
diff --git a/configure.ac b/configure.ac
index 9fabc7bd9..72d1ce542 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1790,9 +1790,6 @@ EL_ARG_ENABLE(CONFIG_SMALL, small, [Small binary],
 EL_ARG_ENABLE(CONFIG_UTF8, utf-8, [UTF-8],
 	      [  --disable-utf-8         disable UTF-8 support])
 
-EL_ARG_DEPEND(CONFIG_COMBINE, combining, [CONFIG_UTF8:yes HAVE_WCWIDTH:yes], [Combining characters],
-	      [  --enable-combining      support Unicode combining characters (experimental)])
-
 EL_ARG_ENABLE(CONFIG_REPRODUCIBLE, reproducible, [Reproducible builds],
 	      [  --enable-reproducible   enable reproducible build])
 
diff --git a/features.conf b/features.conf
index 7c0665854..9281eaa5d 100644
--- a/features.conf
+++ b/features.conf
@@ -680,7 +680,7 @@ CONFIG_SMALL=no
 # support for double-width characters (like Japanese, etc.).
 #
 # Some features of Unicode are not handled at all. Combining characters is
-# most visible absence; but see CONFIG_COMBINE below.
+# most visible absence.
 # Some features are partially supported. Like line breaking between
 # double-width characters. There is no other detection for determining when to
 # break or not.
@@ -691,42 +691,6 @@ CONFIG_SMALL=no
 
 CONFIG_UTF8=yes
 
-
-### Unicode combining characters support
-#
-# Extends CONFIG_UTF8 with spotty support for combining characters
-# such as U+0303 COMBINING TILDE.
-#
-# This feature is experimental and has been filed as enhancement 824.
-# Known bugs and weaknesses:
-#
-# - It assumes wcwidth(wc)==0 means wc is a combining character.
-#   However, wcwidth also returns 0 for various control characters
-#   (e.g. U+200E LEFT-TO-RIGHT MARK), and apparently returns -1 if
-#   LC_CTYPE does not support the wide character.  Besides, wchar_t
-#   might not be Unicode at all.  ELinks should instead use Unicode
-#   character properties, perhaps via ICU.
-#
-# - It assumes all combining characters are nonspacing.
-#
-# - It works only if the terminal is using the UTF-8 charset.
-#
-# - It allocates an internal code for each different combining
-#   character sequence.  A malicious web page could easily use up all
-#   the available codes, and the ELinks process would thenceforth be
-#   unable to display any new sequences.
-#
-# - It does not understand canonical equivalences.
-#
-# - Combining characters work only in HTML text.  They do not work in
-#   HTML forms, HTML links, HTML document titles, plain text, menus,
-#   dialog boxes, or keymaps.
-#
-# Default: disabled
-
-CONFIG_COMBINE=no
-
-
 ### Back-trace Printing
 #
 # Once upon a time, a disaster happens and ELinks crashes. That is a very sad
diff --git a/meson.build b/meson.build
index 91ee0cfde..a3b9f2e25 100644
--- a/meson.build
+++ b/meson.build
@@ -66,7 +66,6 @@ conf_data.set('CONFIG_FASTMEM', get_option('fastmem'))
 conf_data.set('CONFIG_OWN_LIBC', get_option('own-libc'))
 conf_data.set('CONFIG_SMALL', get_option('small'))
 conf_data.set('CONFIG_UTF8', get_option('utf-8'))
-conf_data.set('CONFIG_COMBINE', get_option('combining'))
 
 conf_data.set('CONFIG_XTERM', get_option('xterm'))
 conf_data.set('CONFIG_GPM', get_option('gpm'))
diff --git a/meson_options.txt b/meson_options.txt
index d4c77d3ae..978cd3553 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -37,7 +37,6 @@ option('fastmem', type: 'boolean', value: false, description: 'direct use of sys
 option('own-libc', type: 'boolean', value: false, description: 'force use of internal functions instead of those of system libc')
 option('small', type: 'boolean', value: false, description: 'reduce binary size as far as possible (but see the bottom of doc/small.txt!)')
 option('utf-8', type: 'boolean', value: true, description: 'UTF-8 support')
-option('combining', type: 'boolean', value: false, description: 'support Unicode combining characters (experimental)')
 
 option('xterm', type: 'boolean', value: false, description: 'how to invoke the X terminal emulator')
 option('gpm', type: 'boolean', value: true, description: 'gpm (mouse) support')
diff --git a/src/config/options.inc b/src/config/options.inc
index 59e25431f..12ed6df67 100644
--- a/src/config/options.inc
+++ b/src/config/options.inc
@@ -1077,12 +1077,10 @@ static union option_info config_options_info[] = {
 		"terminal codepage is used. ELinks ignores this option "
 		"if the terminal codepage is UTF-8.")),
 
-#ifdef CONFIG_COMBINE
 	INIT_OPT_BOOL("terminal._template_", N_("Combining characters"),
 		"combine", OPT_ZERO, 0,
 		N_("Enable combining characters. It works only with "
 		"the xterm in UTF-8 mode.")),
-#endif
 
 	INIT_OPT_BOOL("terminal._template_", N_("Restrict frames in cp850/852"),
 		"restrict_852", OPT_ZERO, 0,
diff --git a/src/dialogs/options.c b/src/dialogs/options.c
index 5ee04b5f4..fa91e19d2 100644
--- a/src/dialogs/options.c
+++ b/src/dialogs/options.c
@@ -97,9 +97,7 @@ enum termopt {
 	TERM_OPT_UNDERLINE,
 	TERM_OPT_ITALIC,
 	TERM_OPT_STRIKE,
-#ifdef CONFIG_COMBINE
 	TERM_OPT_COMBINE,
-#endif
 #ifdef CONFIG_LIBSIXEL
 	TERM_OPT_SIXEL,
 #endif
@@ -117,9 +115,7 @@ static struct option_resolver resolvers[] = {
 	{ TERM_OPT_UNDERLINE,	 "underline"	},
 	{ TERM_OPT_ITALIC,	 "italic"	},
 	{ TERM_OPT_STRIKE,	 "strike"	},
-#ifdef CONFIG_COMBINE
 	{ TERM_OPT_COMBINE,	 "combine"	},
-#endif
 #ifdef CONFIG_LIBSIXEL
 	{ TERM_OPT_SIXEL,	 "sixel"	},
 #endif
@@ -246,9 +242,7 @@ terminal_options(struct terminal *term, void *xxx, struct session *ses)
 	add_dlg_checkbox(dlg, _("Underline", term), &values[TERM_OPT_UNDERLINE].number);
 	add_dlg_checkbox(dlg, _("Strikethrough", term), &values[TERM_OPT_STRIKE].number);
 	add_dlg_checkbox(dlg, _("UTF-8 I/O", term), &values[TERM_OPT_UTF_8_IO].number);
-#ifdef CONFIG_COMBINE
 	add_dlg_checkbox(dlg, _("Combining characters", term), &values[TERM_OPT_COMBINE].number);
-#endif
 #ifdef CONFIG_LIBSIXEL
 	add_dlg_checkbox(dlg, _("Sixel", term), &values[TERM_OPT_SIXEL].number);
 #endif
diff --git a/src/document/document.c b/src/document/document.c
index 745034327..f183082bf 100644
--- a/src/document/document.c
+++ b/src/document/document.c
@@ -219,10 +219,6 @@ init_document(struct cache_entry *cached, struct document_options *options)
 	init_list(document->images);
 #endif
 
-#ifdef CONFIG_COMBINE
-	document->comb_x = -1;
-	document->comb_y = -1;
-#endif
 	object_nolock(document, "document");
 	object_lock(document);
 
@@ -365,10 +361,6 @@ reset_document(struct document *document)
 	mem_free_set(&document->slines1, NULL);
 	mem_free_set(&document->slines2, NULL);
 	mem_free_set(&document->search_points, NULL);
-
-#ifdef CONFIG_COMBINE
-	discard_comb_x_y(document);
-#endif
 }
 
 void
diff --git a/src/document/document.h b/src/document/document.h
index 4ffeac050..9cd5ab4a1 100644
--- a/src/document/document.h
+++ b/src/document/document.h
@@ -285,14 +285,6 @@ struct document {
 #ifdef CONFIG_UTF8
 	char buf[7];
 	unsigned char buf_length;
-#endif
-#ifdef CONFIG_COMBINE
-	/* base char + 5 combining chars = 6 */
-	unicode_val_T combi[UCS_MAX_LENGTH_COMBINED];
-	/* the number of combining characters. The base char is not counted. */
-	unsigned int combi_length;
-	/* Positions of the last base character.*/
-	int comb_x, comb_y;
 #endif
 	unsigned int cache_id; /**< Used to check cache entries. */
 
diff --git a/src/document/html/renderer.c b/src/document/html/renderer.c
index 0cf14c87d..8866739fa 100644
--- a/src/document/html/renderer.c
+++ b/src/document/html/renderer.c
@@ -8,15 +8,6 @@
 #include <stdint.h>
 #endif
 
-/* Our current implementation of combining characters requires
- * wcwidth().  Therefore the configure script should have disabled
- * CONFIG_COMBINE if wcwidth() doesn't exist.  */
-#ifdef CONFIG_COMBINE
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 500	/* for wcwidth */
-#endif
-#endif
-
 #include <ctype.h>
 #include <stdarg.h>
 #include <string.h>
@@ -378,108 +369,6 @@ expand_lines(struct html_context *html_context, struct part *part,
 	}
 }
 
-/* document.comb_x and document.comb_y exist only when CONFIG_COMBINE
- * is defined.  assert() does nothing if CONFIG_FASTMEM is defined.  */
-#if defined(CONFIG_COMBINE) && !defined(CONFIG_FASTMEM)
-/** Assert that path->document->comb_x and part->document->comb_y
- * refer to an allocated struct screen_char, or comb_x is -1.
- *
- * The CONFIG_COMBINE variant of set_hline() can update the
- * screen_char.data at these coordinates.  Sometimes, the coordinates
- * have not been valid, and the update has corrupted memory.  These
- * assertions should catch that bug if it happens again.
- *
- * @post This function can leave ::assert_failed set, so the caller
- * should use ::if_assert_failed, perhaps with discard_comb_x_y().  */
-static void
-assert_comb_x_y_ok(const struct document *document)
-{
-	assert(document);
-	if (document->comb_x != -1) {
-		assert(document->comb_y >= 0);
-		assert(document->comb_y < document->height);
-		assert(document->comb_x >= 0);
-		assert(document->comb_x < document->data[document->comb_y].length);
-	}
-}
-#else
-# define assert_comb_x_y_ok(document) ((void) 0)
-#endif
-
-#ifdef CONFIG_COMBINE
-/** Discard any combining characters that have not yet been combined
- * with to the previous base character.  */
-void
-discard_comb_x_y(struct document *document)
-{
-	document->comb_x = -1;
-	document->comb_y = -1;
-	document->combi_length = 0;
-}
-#endif
-
-#ifdef CONFIG_COMBINE
-static void
-move_comb_x_y(struct part *part, int xf, int yf, int xt, int yt)
-{
-	if (part->document->comb_x != -1
-	    && part->document->comb_y == Y(yf)
-	    && part->document->comb_x >= X(xf)) {
-		if (yt >= 0) {
-			part->document->comb_x += xt - xf;
-			part->document->comb_y += yt - yf;
-		} else
-			discard_comb_x_y(part->document);
-	}
-}
-#else
-# define move_comb_x_y(part, xf, yf, xt, yt) ((void) 0)
-#endif
-
-#ifdef CONFIG_COMBINE
-static void
-set_comb_x_y(struct part *part, int x, int y)
-{
-	struct document *document = part->document;
-
-	document->comb_x = X(x);
-	document->comb_y = Y(y);
-	assert_comb_x_y_ok(document);
-	if_assert_failed discard_comb_x_y(document);
-}
-#else
-# define set_comb_x_y(part, x, y) ((void) 0)
-#endif
-
-#ifdef CONFIG_COMBINE
-static void
-put_combined(struct part *part, int x)
-{
-	struct document *document = part->document;
-
-	if (document->combi_length) {
-		if (document->comb_x != -1) {
-			unicode_val_T prev = get_combined(document->combi, document->combi_length + 1);
-
-			assert_comb_x_y_ok(document);
-			if_assert_failed prev = UCS_NO_CHAR;
-
-			/* Make sure the combined character is not considered as
-			 * a space. */
-			if (x)
-				part->spaces[x - 1] = 0;
-
-			if (prev != UCS_NO_CHAR)
-				document->data[document->comb_y]
-					.ch.chars[document->comb_x].data = prev;
-		}
-		document->combi_length = 0;
-	}
-}
-#else
-# define put_combined(part, x) ((void) 0)
-#endif
-
 #ifdef CONFIG_UTF8
 /* First possibly do the format change and then find out what coordinates
  * to use since sub- or superscript might change them */
@@ -523,9 +412,6 @@ set_hline(struct html_context *html_context, const char *chars, int charslen,
 	if (part->document) {
 		struct document *const document = part->document;
 
-		assert_comb_x_y_ok(document);
-		if_assert_failed discard_comb_x_y(document);
-
 		/* Reallocate LINE(y).chars[] to large enough.  The
 		 * last parameter of realloc_line is the index of the
 		 * last element to which we may want to write,
@@ -583,10 +469,7 @@ set_hline(struct html_context *html_context, const char *chars, int charslen,
 					goto good_char;
 				} else {
 					/* Still not full char */
-					assert_comb_x_y_ok(document);
 					LINE(y).length = orig_length;
-					assert_comb_x_y_ok(document);
-					if_assert_failed discard_comb_x_y(document);
 					return 0;
 				}
 			}
@@ -627,38 +510,6 @@ good_char:
 				    && html_context->options->wrap_nbsp)
 					data = UCS_SPACE;
 
-#ifdef CONFIG_COMBINE
-				if (wcwidth((wchar_t)data)) {
-					put_combined(part, x);
-					document->combi[0] = data;
-				} else {
-					if (part->cx == x) {
-						if (X(x)) {
-							/* Isolated combining 
-							 * character not on the 
-							 * first column: combine 
-							 * it with whatever is 
-							 * printed at its left. */
-							document->combi[0] = POS(x - 1, y).data;
-							set_comb_x_y(part, x - 1, y);
-						} else {
-							/* Isolated combining 
-							 * character on the
-							 * first column: use
-							 * UCS_NO_BREAK_SPACE as
-							 * the base character.
-							 * */
-							document->combi[0] = UCS_NO_BREAK_SPACE;
-							set_comb_x_y(part, x, y);
-							schar->data = UCS_SPACE;
-							copy_screen_chars(&POS(x++, y), schar, 1);
-						}
-					}
-					if (document->combi_length < (UCS_MAX_LENGTH_COMBINED - 1))
-						document->combi[++document->combi_length] = data;
-					continue;
-				}
-#endif /* CONFIG_COMBINE */
 				part->spaces[x] = (data == UCS_SPACE);
 
 #ifdef CONFIG_CODEPOINT
@@ -674,10 +525,6 @@ good_char:
 				} else
 #endif
 
-#ifdef CONFIG_COMBINE
-				if (unicode_to_cell(data) == 0)
-					continue;
-#endif
 				if (unicode_to_cell(data) == 2) {
 					schar->data = (unicode_val_T)data;
 					part->char_width[x] = 2;
@@ -689,14 +536,8 @@ good_char:
 					part->char_width[x] = unicode_to_cell(data);
 					schar->data = (unicode_val_T)data;
 				}
-
-				set_comb_x_y(part, x, y);
-
 				copy_screen_chars(&POS(x++, y), schar, 1);
 			} /* while chars < end */
-
-			/* Display any trailing combining characters. */
-			put_combined(part, x);
 		} else { /* not UTF-8 */
 			for (; charslen > 0; charslen--, x++, chars++) {
 				part->char_width[x] = 1;
@@ -724,10 +565,7 @@ good_char:
 		 * which is the number of bytes, not the number of cells.
 		 * Change the length to the correct size, but don't let it
 		 * get smaller than it was on entry to this function.  */
-		assert_comb_x_y_ok(document);
 		LINE(y).length = int_max(orig_length, X(x));
-		assert_comb_x_y_ok(document);
-		if_assert_failed discard_comb_x_y(document);
 		len = x - x2;
 	} else { /* part->document == NULL */
 		if (utf8) {
@@ -737,13 +575,10 @@ good_char:
 				unicode_val_T data;
 
 				data = utf8_to_unicode((char **)&chars, end);
-#ifdef CONFIG_COMBINE
-				if (data == UCS_SOFT_HYPHEN
-				    || (data != UCS_NO_CHAR && wcwidth((wchar_t)data) == 0))
-#else
-				if (data == UCS_SOFT_HYPHEN)
-#endif
+
+				if (data == UCS_SOFT_HYPHEN) {
 					continue;
+				}
 
 				if (data == UCS_NO_BREAK_SPACE
 				    && html_context->options->wrap_nbsp)
@@ -983,11 +818,7 @@ move_chars(struct html_context *html_context, int x, int y, int nx, int ny)
 
 	copy_chars(html_context, nx, ny, LEN(y) - x, &POS(x, y));
 
-	assert_comb_x_y_ok(part->document);
-	move_comb_x_y(part, x, y, nx, ny);
 	LINE(y).length = X(x);
-	assert_comb_x_y_ok(part->document);
-	if_assert_failed discard_comb_x_y(part->document);
 	move_links(html_context, x, y, nx, ny);
 
 	if (par_elformat.blockquote_level && !html_context->table_level) {
@@ -1019,18 +850,11 @@ shift_chars(struct html_context *html_context, int y, int shift)
 
 	copy_screen_chars(a, &POS(0, y), len);
 
-	assert_comb_x_y_ok(part->document);
-	if_assert_failed discard_comb_x_y(part->document);
-
 	clear_hchars(html_context, 0, y, shift, a);
 	copy_chars(html_context, shift, y, len, a);
 	fmem_free(a);
 
 	move_links(html_context, 0, y, shift, y);
-	move_comb_x_y(part, 0, y, shift, y);
-
-	assert_comb_x_y_ok(part->document);
-	if_assert_failed discard_comb_x_y(part->document);
 }
 
 static inline void
@@ -1046,15 +870,8 @@ del_chars(struct html_context *html_context, int x, int y)
 	assert(part && part->document && part->document->data);
 	if_assert_failed return;
 
-	assert_comb_x_y_ok(part->document);
-	if_assert_failed discard_comb_x_y(part->document);
-
 	LINE(y).length = X(x);
-	move_comb_x_y(part, x, y, -1, -1);
 	move_links(html_context, x, y, -1, -1);
-
-	assert_comb_x_y_ok(part->document);
-	if_assert_failed discard_comb_x_y(part->document);
 }
 
 #if TABLE_LINE_PADDING < 0
@@ -1379,7 +1196,6 @@ justify_line(struct html_context *html_context, int y)
 			 * link.  */
 			new_spaces = new_start - prev_end - 1;
 			if (word && new_spaces) {
-				move_comb_x_y(part, prev_end + 1, y, new_start, y);
 				move_links(html_context, prev_end + 1, y, new_start, y);
 				insert_spaces_in_link(part,
 						      new_start, y, new_spaces);
diff --git a/src/document/html/renderer.h b/src/document/html/renderer.h
index 97ce90120..cb754001d 100644
--- a/src/document/html/renderer.h
+++ b/src/document/html/renderer.h
@@ -123,14 +123,6 @@ void line_break(struct html_context *html_context);
 
 void *html_special(struct html_context *html_context, html_special_type_T c, ...);
 
-#ifdef CONFIG_COMBINE
-/** Discard any combining characters that have not yet been combined
- * with to the previous base character.  */
-void discard_comb_x_y(struct document *document);
-#else
-# define discard_comb_x_y(document) ((void) 0)
-#endif
-
 
 #ifdef __cplusplus
 }
diff --git a/src/intl/charsets.c b/src/intl/charsets.c
index 509db7743..9a1ebe832 100644
--- a/src/intl/charsets.c
+++ b/src/intl/charsets.c
@@ -869,69 +869,6 @@ cp_to_unicode(int codepage, char **string, const char *end)
 	return ret;
 }
 
-
-#ifdef CONFIG_COMBINE
-unicode_val_T last_combined = UCS_BEGIN_COMBINED - 1;
-unicode_val_T **combined;
-struct hash *combined_hash;
-
-unicode_val_T
-get_combined(unicode_val_T *data, int length)
-{
-	struct hash_item *item;
-	unicode_val_T *key;
-	int i, indeks;
-
-	assert(length >= 1 && length <= UCS_MAX_LENGTH_COMBINED);
-	if_assert_failed return UCS_NO_CHAR;
-
-	if (!combined_hash) combined_hash = init_hash8();
-	if (!combined_hash) return UCS_NO_CHAR;
-	item = get_hash_item(combined_hash, (char *)data, length * sizeof(*data));
-
-	if (item) return (unicode_val_T)(intptr_t)item->value;
-	if (last_combined >= UCS_END_COMBINED) return UCS_NO_CHAR;
-
-	key = (unicode_val_T *)mem_alloc((length + 1) * sizeof(*key));
-	if (!key) return UCS_NO_CHAR;
-	for (i = 0; i < length; i++)
-		key[i] = data[i];
-	key[i] = UCS_END_COMBINED;
-
-	last_combined++;
-	indeks = last_combined - UCS_BEGIN_COMBINED;
-
-	combined = (unicode_val_T **)mem_realloc(combined, sizeof(*combined) * (indeks + 1));
-	if (!combined) {
-		mem_free(key);
-		last_combined--;
-		return UCS_NO_CHAR;
-	}
-	combined[indeks] = key;
-	item = add_hash_item(combined_hash, (char *)key,
-			     length * sizeof(*data), (void *)(intptr_t)(last_combined));
-	if (!item) {
-		last_combined--;
-		mem_free(key);
-		return UCS_NO_CHAR;
-	}
-	return last_combined;
-}
-
-void
-free_combined()
-{
-	int i, end = last_combined - UCS_BEGIN_COMBINED + 1; 
-
-	if (combined_hash)
-		free_hash(&combined_hash);
-	for (i = 0; i < end; i++)
-		mem_free(combined[i]);
-	mem_free_if(combined);
-}
-#endif /* CONFIG_COMBINE */
-
-
 static void
 add_utf8(struct conv_table *ct, unicode_val_T u, const char *str)
 {
diff --git a/src/intl/charsets.h b/src/intl/charsets.h
index 7cfe3fc5f..6bfe1cc7c 100644
--- a/src/intl/charsets.h
+++ b/src/intl/charsets.h
@@ -33,15 +33,6 @@ typedef uint32_t unicode_val_T;
  * for the second cell of a double-cell character.  */
 #define UCS_NO_CHAR ((unicode_val_T) 0xFFFFFFFD)
 
-#ifdef CONFIG_COMBINE
-#define UCS_END_COMBINED ((unicode_val_T) 0xFFFFFFFC)
-
-#define UCS_BEGIN_COMBINED ((unicode_val_T) (UCS_END_COMBINED - (unicode_val_T) 10000))
-
-/* Base character and up to 5 combining characters. */
-#define UCS_MAX_LENGTH_COMBINED 6
-#endif /* CONFIG_COMBINE */
-
 /* If ELinks should display a double-cell character but there is only
  * one cell available, it displays this character instead.  This must
  * be a single-cell character but need not be unique.  Possible values
@@ -168,14 +159,6 @@ int strlen_utf8(char **);
 unicode_val_T utf8_to_unicode(char **, const char *);
 unicode_val_T cp_to_unicode(int, char **, const char *);
 
-#ifdef CONFIG_COMBINE
-extern unicode_val_T last_combined;
-extern unicode_val_T **combined;
-extern struct hash *combined_hash;
-unicode_val_T get_combined(unicode_val_T *, int);
-void free_combined();
-#endif /* CONFIG_COMBINE */
-
 unicode_val_T cp2u(int, unsigned char);
 const char *cp2utf8(int, int);
 
diff --git a/src/main/main.c b/src/main/main.c
index 75ad11187..4ffbd82c3 100644
--- a/src/main/main.c
+++ b/src/main/main.c
@@ -332,9 +332,6 @@ terminate_all_subsystems(void)
 	done_event();
 	terminate_select();
 	terminate_osdep();
-#ifdef CONFIG_COMBINE
-	free_combined();
-#endif
 #ifdef CONFIG_LIBDOM
 	free_libdom();
 #endif
diff --git a/src/main/version.c b/src/main/version.c
index 48f1d03c5..ea4e6e0ec 100644
--- a/src/main/version.c
+++ b/src/main/version.c
@@ -185,9 +185,6 @@ get_dyn_full_version(struct terminal *term, int more)
 #ifdef CONFIG_UTF8
 		comma, "UTF-8",
 #endif
-#ifdef CONFIG_COMBINE
-		comma, _("Combining characters", term),
-#endif
 #ifdef CONFIG_LIBEV
 		comma, (event_enabled ? _("libev", term) : _("libev (disabled)", term)), "(", get_libevent_version(), ")",
 #endif
diff --git a/src/terminal/screen.c b/src/terminal/screen.c
index f9b75eed6..e326bbf7c 100644
--- a/src/terminal/screen.c
+++ b/src/terminal/screen.c
@@ -271,10 +271,8 @@ struct screen_driver_opt {
 	unsigned int utf8_cp:1;
 #endif /* CONFIG_UTF8 */
 
-#ifdef CONFIG_COMBINE
 	/* Whether the terminal supports combining characters. */
 	unsigned int combine:1;
-#endif /* CONFIG_COMBINE */
 
 #ifdef CONFIG_TERMINFO
 	/* Whether use terminfo. */
@@ -321,9 +319,7 @@ static const struct screen_driver_opt dumb_screen_driver_opt = {
 #ifdef CONFIG_UTF8
 	/* utf8_cp: */		0,
 #endif /* CONFIG_UTF8 */
-#ifdef CONFIG_COMBINE
 	/* combine */		0,
-#endif /* CONFIG_COMBINE */
 #ifdef CONFIG_TERMINFO
 	/* terminfo */		0,
 #endif
@@ -348,9 +344,7 @@ static const struct screen_driver_opt vt100_screen_driver_opt = {
 #ifdef CONFIG_UTF8
 	/* utf8_cp: */		0,
 #endif /* CONFIG_UTF8 */
-#ifdef CONFIG_COMBINE
 	/* combine */		0,
-#endif /* CONFIG_COMBINE */
 #ifdef CONFIG_TERMINFO
 	/* terminfo */		0,
 #endif
@@ -375,9 +369,7 @@ static const struct screen_driver_opt linux_screen_driver_opt = {
 #ifdef CONFIG_UTF8
 	/* utf8_cp: */		0,
 #endif /* CONFIG_UTF8 */
-#ifdef CONFIG_COMBINE
 	/* combine */		0,
-#endif /* CONFIG_COMBINE */
 #ifdef CONFIG_TERMINFO
 	/* terminfo */		0,
 #endif
@@ -402,9 +394,7 @@ static const struct screen_driver_opt koi8_screen_driver_opt = {
 #ifdef CONFIG_UTF8
 	/* utf8_cp: */		0,
 #endif /* CONFIG_UTF8 */
-#ifdef CONFIG_COMBINE
 	/* combine */		0,
-#endif /* CONFIG_COMBINE */
 #ifdef CONFIG_TERMINFO
 	/* terminfo */		0,
 #endif
@@ -429,9 +419,7 @@ static const struct screen_driver_opt freebsd_screen_driver_opt = {
 #ifdef CONFIG_UTF8
 	/* utf8_cp: */		0,
 #endif /* CONFIG_UTF8 */
-#ifdef CONFIG_COMBINE
 	/* combine */		0,
-#endif /* CONFIG_COMBINE */
 #ifdef CONFIG_TERMINFO
 	/* terminfo */		0,
 #endif
@@ -456,9 +444,7 @@ static const struct screen_driver_opt fbterm_screen_driver_opt = {
 #ifdef CONFIG_UTF8
 	/* utf8_cp: */		0,
 #endif /* CONFIG_UTF8 */
-#ifdef CONFIG_COMBINE
 	/* combine */		0,
-#endif /* CONFIG_COMBINE */
 #ifdef CONFIG_TERMINFO
 	/* terminfo */		0,
 #endif
@@ -510,9 +496,7 @@ set_screen_driver_opt(struct screen_driver *driver, struct option *term_spec)
 	 * function need not carefully restore options one by one.  */
 	copy_struct(&driver->opt, screen_driver_opts[driver->type]);
 
-#ifdef CONFIG_COMBINE
 	driver->opt.combine = get_opt_bool_tree(term_spec, "combine", NULL);
-#endif /* CONFIG_COMBINE */
 
 #ifdef CONFIG_LIBSIXEL
 	driver->opt.sixel = get_opt_bool_tree(term_spec, "sixel", NULL);
@@ -858,23 +842,7 @@ add_char_data(struct string *screen, struct screen_driver *driver,
 		}
 		if (data == UCS_NO_CHAR)
 			return;
-#ifdef CONFIG_COMBINE
-		if (data >= UCS_BEGIN_COMBINED && data <= last_combined) {
-			unicode_val_T *text = combined[data - UCS_BEGIN_COMBINED];
 
-			if (driver->opt.combine) {
-				/* XTerm */
-				while (*text != UCS_END_COMBINED) {
-					add_to_string(screen, encode_utf8(*text));
-					text++;
-				}
-				return;
-			} else {
-				/* Others */
-				data = *text;
-			}
-		}
-#endif /* CONFIG_COMBINE */
 		if (!isscreensafe_ucs(data))
 			data = UCS_SPACE;
 		add_to_string(screen, encode_utf8(data));