diff --git a/configlexer.lex b/configlexer.lex
index 113fa22..03903de 100644
--- a/configlexer.lex
+++ b/configlexer.lex
@@ -268,6 +268,10 @@ zonefiles-check{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_CHECK;
 zonefiles-write{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ZONEFILES_WRITE;}
 log-time-ascii{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_LOG_TIME_ASCII;}
 round-robin{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_ROUND_ROBIN;}
+max-refresh-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MAX_REFRESH_TIME;}
+min-refresh-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MIN_REFRESH_TIME;}
+max-retry-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MAX_RETRY_TIME;}
+min-retry-time{COLON}	{ LEXOUT(("v(%s) ", yytext)); return VAR_MIN_RETRY_TIME;}
 {NEWLINE}		{ LEXOUT(("NL\n")); cfg_parser->line++;}
 
 	/* Quoted strings. Strip leading and ending quotes */
diff --git a/configparser.y b/configparser.y
index 7ebf782..e0f9c15 100644
--- a/configparser.y
+++ b/configparser.y
@@ -69,6 +69,8 @@ extern config_parser_state_t* cfg_parser;
 %token VAR_RRL_WHITELIST_RATELIMIT VAR_RRL_WHITELIST
 %token VAR_ZONEFILES_CHECK VAR_ZONEFILES_WRITE VAR_LOG_TIME_ASCII
 %token VAR_ROUND_ROBIN VAR_ZONESTATS VAR_REUSEPORT VAR_VERSION
+%token VAR_MAX_REFRESH_TIME VAR_MIN_REFRESH_TIME
+%token VAR_MAX_RETRY_TIME VAR_MIN_RETRY_TIME
 
 %%
 toplevelvars: /* empty */ | toplevelvars toplevelvar ;
@@ -598,7 +600,8 @@ content_pattern: pattern_name | zone_config_item;
 zone_config_item: zone_zonefile | zone_allow_notify | zone_request_xfr |
 	zone_notify | zone_notify_retry | zone_provide_xfr | 
 	zone_outgoing_interface | zone_allow_axfr_fallback | include_pattern |
-	zone_rrl_whitelist | zone_zonestats;
+	zone_rrl_whitelist | zone_zonestats | zone_max_refresh_time |
+	zone_min_refresh_time | zone_max_retry_time | zone_min_retry_time;
 pattern_name: VAR_NAME STRING
 	{ 
 		OUTYY(("P(pattern_name:%s)\n", $2)); 
@@ -819,6 +822,46 @@ zone_rrl_whitelist: VAR_RRL_WHITELIST STRING
 #endif
 	}
 	;
+zone_max_refresh_time: VAR_MAX_REFRESH_TIME STRING
+{
+	OUTYY(("P(zone_max_refresh_time:%s)\n", $2));
+	if(atoi($2) == 0 && strcmp($2, "0") != 0)
+		yyerror("number expected");
+	else {
+		cfg_parser->current_pattern->max_refresh_time = atoi($2);
+		cfg_parser->current_pattern->max_refresh_time_is_default = 0;
+	}
+};
+zone_min_refresh_time: VAR_MIN_REFRESH_TIME STRING
+{
+	OUTYY(("P(zone_min_refresh_time:%s)\n", $2));
+	if(atoi($2) == 0 && strcmp($2, "0") != 0)
+		yyerror("number expected");
+	else {
+		cfg_parser->current_pattern->min_refresh_time = atoi($2);
+		cfg_parser->current_pattern->min_refresh_time_is_default = 0;
+	}
+};
+zone_max_retry_time: VAR_MAX_RETRY_TIME STRING
+{
+	OUTYY(("P(zone_max_retry_time:%s)\n", $2));
+	if(atoi($2) == 0 && strcmp($2, "0") != 0)
+		yyerror("number expected");
+	else {
+		cfg_parser->current_pattern->max_retry_time = atoi($2);
+		cfg_parser->current_pattern->max_retry_time_is_default = 0;
+	}
+};
+zone_min_retry_time: VAR_MIN_RETRY_TIME STRING
+{
+	OUTYY(("P(zone_min_retry_time:%s)\n", $2));
+	if(atoi($2) == 0 && strcmp($2, "0") != 0)
+		yyerror("number expected");
+	else {
+		cfg_parser->current_pattern->min_retry_time = atoi($2);
+		cfg_parser->current_pattern->min_retry_time_is_default = 0;
+	}
+};
 
 /* key: declaration */
 keystart: VAR_KEY
diff --git a/nsd-checkconf.c b/nsd-checkconf.c
index e5f669f..9525c56 100644
--- a/nsd-checkconf.c
+++ b/nsd-checkconf.c
@@ -59,6 +59,12 @@ extern int optind;
 		return;					\
 	}
 
+#define ZONE_GET_INT(NAME, VAR, PATTERN) 		\
+	if (strcasecmp(#NAME, (VAR)) == 0) { 	\
+		printf("%d\n", (int) PATTERN->NAME); 	\
+		return; 			\
+	}
+
 #define SERV_GET_BIN(NAME, VAR) 			\
 	if (strcasecmp(#NAME, (VAR)) == 0) { 		\
 		printf("%s\n", opt->NAME?"yes":"no"); 	\
@@ -306,6 +312,10 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o,
 		ZONE_GET_STR(zonestats, o, zone->pattern);
 		ZONE_GET_OUTGOING(outgoing_interface, o, zone->pattern);
 		ZONE_GET_BIN(allow_axfr_fallback, o, zone->pattern);
+		ZONE_GET_INT(max_refresh_time, o, zone->pattern);
+		ZONE_GET_INT(min_refresh_time, o, zone->pattern);
+		ZONE_GET_INT(max_retry_time, o, zone->pattern);
+		ZONE_GET_INT(min_retry_time, o, zone->pattern);
 #ifdef RATELIMIT
 		ZONE_GET_RRL(rrl_whitelist, o, zone->pattern);
 #endif
@@ -331,6 +341,10 @@ config_print_zone(nsd_options_t* opt, const char* k, int s, const char *o,
 		ZONE_GET_STR(zonestats, o, p);
 		ZONE_GET_OUTGOING(outgoing_interface, o, p);
 		ZONE_GET_BIN(allow_axfr_fallback, o, p);
+		ZONE_GET_INT(max_refresh_time, o, p);
+		ZONE_GET_INT(min_refresh_time, o, p);
+		ZONE_GET_INT(max_retry_time, o, p);
+		ZONE_GET_INT(min_retry_time, o, p);
 #ifdef RATELIMIT
 		ZONE_GET_RRL(rrl_whitelist, o, p);
 #endif
@@ -431,6 +445,14 @@ static void print_zone_content_elems(pattern_options_t* pat)
 	if(!pat->allow_axfr_fallback_is_default)
 		printf("\tallow-axfr-fallback: %s\n",
 			pat->allow_axfr_fallback?"yes":"no");
+	if(!pat->max_refresh_time_is_default)
+		printf("\tmax-refresh-time: %d\n", pat->max_refresh_time);
+	if(!pat->min_refresh_time_is_default)
+		printf("\tmin-refresh-time: %d\n", pat->min_refresh_time);
+	if(!pat->max_retry_time_is_default)
+		printf("\tmax-retry-time: %d\n", pat->max_retry_time);
+	if(!pat->min_retry_time_is_default)
+		printf("\tmin-retry-time: %d\n", pat->min_retry_time);
 }
 
 void
diff --git a/nsd.conf.sample.in b/nsd.conf.sample.in
index 48eef14..a55816e 100644
--- a/nsd.conf.sample.in
+++ b/nsd.conf.sample.in
@@ -258,6 +258,11 @@ remote-control:
 	# set local interface for sending zone transfer requests.
 	# default is let the OS choose.
 	#outgoing-interface: 10.0.0.10
+	# limit the refresh and retry interval in seconds.
+	#max-refresh-time: 2419200
+	#min-refresh-time: 300
+	#max-retry-time: 1209600
+	#min-retry-time: 500
 
 	# if compiled with --enable-zone-stats, give name of stat block for
 	# this zone (or group of zones).  Output from nsd-control stats.
diff --git a/options.c b/options.c
index eabc8bf..e211af9 100644
--- a/options.c
+++ b/options.c
@@ -822,6 +822,14 @@ pattern_options_create(region_type* region)
 	p->allow_axfr_fallback_is_default = 1;
 	p->implicit = 0;
 	p->xfrd_flags = 0;
+	p->max_refresh_time = 2419200;	/* 4 weeks */
+	p->max_refresh_time_is_default = 1;
+	p->min_refresh_time = 300;
+	p->min_refresh_time_is_default = 1;
+	p->max_retry_time = 1209600;	/* 2 weeks */
+	p->max_retry_time_is_default = 1;
+	p->min_retry_time = 500;
+	p->min_retry_time_is_default = 1;
 #ifdef RATELIMIT
 	p->rrl_whitelist = 0;
 #endif
@@ -944,6 +952,14 @@ copy_pat_fixed(region_type* region, pattern_options_t* orig,
 	if(p->zonestats)
 		orig->zonestats = region_strdup(region, p->zonestats);
 	else orig->zonestats = NULL;
+	orig->max_refresh_time = p->max_refresh_time;
+	orig->max_refresh_time_is_default = p->max_refresh_time_is_default;
+	orig->min_refresh_time = p->min_refresh_time;
+	orig->min_refresh_time_is_default = p->min_refresh_time_is_default;
+	orig->max_retry_time = p->max_retry_time;
+	orig->max_retry_time_is_default = p->max_retry_time_is_default;
+	orig->min_retry_time = p->min_retry_time;
+	orig->min_retry_time_is_default = p->min_retry_time_is_default;
 #ifdef RATELIMIT
 	orig->rrl_whitelist = p->rrl_whitelist;
 #endif
@@ -1017,6 +1033,18 @@ pattern_options_equal(pattern_options_t* p, pattern_options_t* q)
 	if(!acl_list_equal(p->provide_xfr, q->provide_xfr)) return 0;
 	if(!acl_list_equal(p->outgoing_interface, q->outgoing_interface))
 		return 0;
+	if(p->max_refresh_time != q->max_refresh_time) return 0;
+	if(!booleq(p->max_refresh_time_is_default,
+		q->max_refresh_time_is_default)) return 0;
+	if(p->min_refresh_time != q->min_refresh_time) return 0;
+	if(!booleq(p->min_refresh_time_is_default,
+		q->min_refresh_time_is_default)) return 0;
+	if(p->max_retry_time != q->max_retry_time) return 0;
+	if(!booleq(p->max_retry_time_is_default,
+		q->max_retry_time_is_default)) return 0;
+	if(p->min_retry_time != q->min_retry_time) return 0;
+	if(!booleq(p->min_retry_time_is_default,
+		q->min_retry_time_is_default)) return 0;
 #ifdef RATELIMIT
 	if(p->rrl_whitelist != q->rrl_whitelist) return 0;
 #endif
@@ -1054,6 +1082,19 @@ unmarshal_u16(struct buffer* b)
 #endif
 
 static void
+marshal_u32(struct buffer* b, uint32_t v)
+{
+	buffer_reserve(b, 4);
+	buffer_write_u32(b, v);
+}
+
+static uint32_t
+unmarshal_u32(struct buffer* b)
+{
+	return buffer_read_u32(b);
+}
+
+static void
 marshal_str(struct buffer* b, const char* s)
 {
 	if(!s) marshal_u8(b, 0);
@@ -1143,6 +1184,14 @@ pattern_options_marshal(struct buffer* b, pattern_options_t* p)
 	marshal_acl_list(b, p->notify);
 	marshal_acl_list(b, p->provide_xfr);
 	marshal_acl_list(b, p->outgoing_interface);
+	marshal_u32(b, p->max_refresh_time);
+	marshal_u8(b, p->max_refresh_time_is_default);
+	marshal_u32(b, p->min_refresh_time);
+	marshal_u8(b, p->min_refresh_time_is_default);
+	marshal_u32(b, p->max_retry_time);
+	marshal_u8(b, p->max_retry_time_is_default);
+	marshal_u32(b, p->min_retry_time);
+	marshal_u8(b, p->min_retry_time_is_default);
 }
 
 pattern_options_t*
@@ -1165,6 +1214,14 @@ pattern_options_unmarshal(region_type* r, struct buffer* b)
 	p->notify = unmarshal_acl_list(r, b);
 	p->provide_xfr = unmarshal_acl_list(r, b);
 	p->outgoing_interface = unmarshal_acl_list(r, b);
+	p->max_refresh_time = unmarshal_u32(b);
+	p->max_refresh_time_is_default = unmarshal_u8(b);
+	p->min_refresh_time = unmarshal_u32(b);
+	p->min_refresh_time_is_default = unmarshal_u8(b);
+	p->max_retry_time = unmarshal_u32(b);
+	p->max_retry_time_is_default = unmarshal_u8(b);
+	p->min_retry_time = unmarshal_u32(b);
+	p->min_retry_time_is_default = unmarshal_u8(b);
 	return p;
 }
 
@@ -1875,6 +1932,22 @@ config_apply_pattern(const char* name)
 		a->notify_retry = pat->notify_retry;
 		a->notify_retry_is_default = 0;
 	}
+	if(!pat->max_refresh_time_is_default) {
+		a->max_refresh_time = pat->max_refresh_time;
+		a->max_refresh_time_is_default = 0;
+	}
+	if(!pat->min_refresh_time_is_default) {
+		a->min_refresh_time = pat->min_refresh_time;
+		a->min_refresh_time_is_default = 0;
+	}
+	if(!pat->max_retry_time_is_default) {
+		a->max_retry_time = pat->max_retry_time;
+		a->max_retry_time_is_default = 0;
+	}
+	if(!pat->min_refresh_time_is_default) {
+		a->min_retry_time = pat->min_retry_time;
+		a->min_retry_time_is_default = 0;
+	}
 #ifdef RATELIMIT
 	a->rrl_whitelist |= pat->rrl_whitelist;
 #endif
diff --git a/options.h b/options.h
index ceba624..3f500ed 100644
--- a/options.h
+++ b/options.h
@@ -154,6 +154,14 @@ struct pattern_options {
 	uint8_t notify_retry_is_default;
 	uint8_t implicit; /* pattern is implicit, part_of_config zone used */
 	uint8_t xfrd_flags;
+	uint32_t max_refresh_time;
+	uint8_t max_refresh_time_is_default;
+	uint32_t min_refresh_time;
+	uint8_t min_refresh_time_is_default;
+	uint32_t max_retry_time;
+	uint8_t max_retry_time_is_default;
+	uint32_t min_retry_time;
+	uint8_t min_retry_time_is_default;
 };
 
 #define PATTERN_IMPLICIT_MARKER "_implicit_"
diff --git a/xfrd-disk.c b/xfrd-disk.c
index 3fa8630..b23a619 100644
--- a/xfrd-disk.c
+++ b/xfrd-disk.c
@@ -147,6 +147,7 @@ xfrd_read_state(struct xfrd_state* xfrd)
 	uint32_t filetime = 0;
 	uint32_t numzones, i;
 	region_type *tempregion;
+	time_t soa_refresh;
 
 	tempregion = region_create(xalloc, free);
 	if(!tempregion)
@@ -265,10 +266,15 @@ xfrd_read_state(struct xfrd_state* xfrd)
 		 * or there is a notification,
 		 * or there is a soa && current time is past refresh point
 		 */
+		soa_refresh = ntohl(soa_disk_read.refresh);
+		if (soa_refresh > zone->zone_options->pattern->max_refresh_time)
+			soa_refresh = zone->zone_options->pattern->max_refresh_time;
+		else if (soa_refresh < zone->zone_options->pattern->min_refresh_time)
+			soa_refresh = zone->zone_options->pattern->min_refresh_time;
 		if(timeout == 0 || soa_notified_acquired_read != 0 ||
 			(soa_disk_acquired_read != 0 &&
 			(uint32_t)xfrd_time() - soa_disk_acquired_read
-				> ntohl(soa_disk_read.refresh)))
+				> soa_refresh))
 		{
 			zone->state = xfrd_zone_refreshing;
 			xfrd_set_refresh_now(zone);
diff --git a/xfrd.c b/xfrd.c
index c2c75ed..2a55421 100644
--- a/xfrd.c
+++ b/xfrd.c
@@ -702,7 +702,12 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone)
 		return;
 	}
 	/* refresh or expire timeout, whichever is earlier */
-	set_refresh = zone->soa_disk_acquired + ntohl(zone->soa_disk.refresh);
+	set_refresh = ntohl(zone->soa_disk.refresh);
+	if (set_refresh > zone->zone_options->pattern->max_refresh_time)
+		set_refresh = zone->zone_options->pattern->max_refresh_time;
+	else if (set_refresh < zone->zone_options->pattern->min_refresh_time)
+		set_refresh = zone->zone_options->pattern->min_refresh_time;
+	set_refresh += zone->soa_disk_acquired;
 	set_expire = zone->soa_disk_acquired + ntohl(zone->soa_disk.expire);
 	if(set_refresh < set_expire)
 		set = set_refresh;
@@ -719,6 +724,7 @@ xfrd_set_timer_refresh(xfrd_zone_t* zone)
 static void
 xfrd_set_timer_retry(xfrd_zone_t* zone)
 {
+	time_t set_retry;
 	/* set timer for next retry or expire timeout if earlier. */
 	if(zone->soa_disk_acquired == 0) {
 		/* if no information, use reasonable timeout */
@@ -743,10 +749,14 @@ xfrd_set_timer_retry(xfrd_zone_t* zone)
 		xfrd_time() + (time_t)ntohl(zone->soa_disk.retry) <
 		zone->soa_disk_acquired + (time_t)ntohl(zone->soa_disk.expire))
 	{
-		if(ntohl(zone->soa_disk.retry) < XFRD_LOWERBOUND_RETRY)
-			xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
-		else
-			xfrd_set_timer(zone, ntohl(zone->soa_disk.retry));
+		set_retry = ntohl(zone->soa_disk.retry);
+		if(set_retry > zone->zone_options->pattern->max_retry_time)
+			set_retry = zone->zone_options->pattern->max_retry_time;
+		else if(set_retry < zone->zone_options->pattern->min_retry_time)
+			set_retry = zone->zone_options->pattern->min_retry_time;
+		if(set_retry < XFRD_LOWERBOUND_RETRY)
+			set_retry =  XFRD_LOWERBOUND_RETRY;
+		xfrd_set_timer(zone, set_retry);
 	} else {
 		if(ntohl(zone->soa_disk.expire) < XFRD_LOWERBOUND_RETRY)
 			xfrd_set_timer(zone, XFRD_LOWERBOUND_RETRY);
